ChatGPT解决这个技术问题 Extra ChatGPT

如何将 UTF-8 字节 [] 转换为字符串

我有一个 byte[] 数组,它是从我碰巧知道的包含 UTF-8 的文件中加载的。

在一些调试代码中,我需要将其转换为字符串。是否有单线可以做到这一点?

在幕后它应该只是一个分配和一个内存副本,所以即使它没有实现,它也应该是可能的。

“应该只是一个分配和一个内存副本”:不正确,因为 .NET 字符串是 UTF-16 编码的。一个 Unicode 字符可能是一个 UTF-8 代码单元或一个 UTF-16 代码单元。另一个可能是两个 UTF-8 代码单元或一个 UTF-16 代码单元,另一个可能是三个 UTF-8 代码单元或一个 UTF-16 代码单元,另一个可能是四个 UTF-8 代码单元或两个 UTF-16 代码单元. memcopy 可能能够扩大,但无法处理 UTF-8 到 UTF-16 的转换。

J
James Webster
string result = System.Text.Encoding.UTF8.GetString(byteArray);

它如何处理以空结尾的字符串?
@maazza 出于未知原因,它根本没有。我称它为 System.Text.Encoding.UTF8.GetString(buf).TrimEnd('\0');
@Hi-Angel 未知原因?空终止字符串变得流行的唯一原因是 C 语言 - 甚至那只是因为历史上的怪异(处理空终止字符串的 CPU 指令)。 .NET 仅在与使用空终止字符串(最终消失)的代码互操作时使用空终止字符串。字符串包含 NUL 字符是完全有效的。当然,虽然以空结尾的字符串在 ASCII 中非常简单(只需构建直到获得第一个零字节),其他编码,包括 UTF-8,就没有那么简单了。
UTF-8 的一大优点是较短的序列永远不是较长序列的子序列。所以一个空终止的 UTF-8 字符串很简单。
好吧,如果它有非ascii,祝你好运。只需使用 Convert.ToBase64String。
A
Ali

至少有四种不同的方式进行这种转换。

编码的 GetString ,但如果这些字节包含非 ASCII 字符,您将无法取回原始字节。 BitConverter.ToString 输出是一个以“-”分隔的字符串,但没有 .NET 内置方法将字符串转换回字节数组。 Convert.ToBase64String 您可以使用 Convert.FromBase64String 轻松地将输出字符串转换回字节数组。注意:输出字符串可以包含“+”、“/”和“=”。如果要在 URL 中使用字符串,则需要对其进行显式编码。 HttpServerUtility.UrlTokenEncode 您可以使用 HttpServerUtility.UrlTokenDecode 轻松地将输出字符串转换回字节数组。输出字符串已经是 URL 友好的!缺点是如果您的项目不是 Web 项目,它需要 System.Web 程序集。

一个完整的例子:

byte[] bytes = { 130, 200, 234, 23 }; // A byte array contains non-ASCII (or non-readable) characters

string s1 = Encoding.UTF8.GetString(bytes); // ���
byte[] decBytes1 = Encoding.UTF8.GetBytes(s1);  // decBytes1.Length == 10 !!
// decBytes1 not same as bytes
// Using UTF-8 or other Encoding object will get similar results

string s2 = BitConverter.ToString(bytes);   // 82-C8-EA-17
String[] tempAry = s2.Split('-');
byte[] decBytes2 = new byte[tempAry.Length];
for (int i = 0; i < tempAry.Length; i++)
    decBytes2[i] = Convert.ToByte(tempAry[i], 16);
// decBytes2 same as bytes

string s3 = Convert.ToBase64String(bytes);  // gsjqFw==
byte[] decByte3 = Convert.FromBase64String(s3);
// decByte3 same as bytes

string s4 = HttpServerUtility.UrlTokenEncode(bytes);    // gsjqFw2
byte[] decBytes4 = HttpServerUtility.UrlTokenDecode(s4);
// decBytes4 same as bytes

LINQ 它:var decBytes2 = str.Split('-').Select(ch => Convert.ToByte(ch, 16)).ToArray();
这应该是公认的答案。它完美地说明了多种方法的输出。当前接受的答案只显示一个,这对于一些不会向下滚动这么远的开发人员来说可能会有问题。 - 当然,除非你按选票排序。
s
slavoo

当您不知道编码时,从字节数组转换为字符串的一般解决方案:

static string BytesToStringConverted(byte[] bytes)
{
    using (var stream = new MemoryStream(bytes))
    {
        using (var streamReader = new StreamReader(stream))
        {
            return streamReader.ReadToEnd();
        }
    }
}

但这假设字节流中有编码 BOM 或者它是 UTF-8。但是无论如何你都可以对 Encoding 做同样的事情。当您不知道编码时,它并不能神奇地解决问题。
P
Peter Mortensen

定义:

public static string ConvertByteToString(this byte[] source)
{
    return source != null ? System.Text.Encoding.UTF8.GetString(source) : null;
}

使用:

string result = input.ConvertByteToString();

P
Peter Mortensen

byte[] 转换为 string 似乎很简单,但任何类型的编码都可能会弄乱输出字符串。这个小功能可以正常工作而没有任何意外结果:

private string ToString(byte[] bytes)
{
    string response = string.Empty;

    foreach (byte b in bytes)
        response += (Char)b;

    return response;
}

当我用 Convert.FromBase64String 解压它时,我使用你的方法收到了 System.FormatException。
@AndrewJE 如果您有一个像图片中使用的大字节数组,这将需要计算。
P
Peter Mortensen

我在这篇文章中看到了一些答案,这可能被认为是完整的基础知识,因为我在 C# 编程中有几种方法可以解决相同的问题。唯一需要考虑的是 pure UTF-8 和带有 BOMUTF-8 之间的区别。

上周,在我的工作中,我需要开发一种功能来输出带有 BOM 的 CSV 文件和带有纯 UTF-8(没有 BOM)的其他 CSV 文件。每个 CSV 文件 encoding 类型将被不同的非标准化 API 使用。一个 API 读取带有 BOM 的 UTF-8,而另一个 API 读取没有 BOM。我需要研究有关此概念的参考资料,阅读 "What's the difference between UTF-8 and UTF-8 without BOM?" Stack Overflow 问题和维基百科文章 "Byte order mark" 来构建我的方法。

最后,我的两种 UTF-8 编码类型(使用 BOM 和纯编码)的 C# 编程需要类似于下面的示例:

// For UTF-8 with BOM, equals shared by Zanoni (at top)
string result = System.Text.Encoding.UTF8.GetString(byteArray);

//for Pure UTF-8 (without B.O.M.)
string result = (new UTF8Encoding(false)).GetString(byteArray);

您不需要从一开始就专门剥离 BOM 吗?据我所知,即使您使用带有 BOM 的 UTF8Encoding,它也不会自动将其删除。
@Nyerguds,参数值为“false”的 UTF8Encoding 对象没有 BOM。
不,我的意思是,如果文本有 BOM,即使是 System.Text.Encoding.UTF8不会自动将其去掉。试试看。
m
metadings

使用 (byte)b.ToString("x2"),输出 b4b5dfe475e58b67

public static class Ext {

    public static string ToHexString(this byte[] hex)
    {
        if (hex == null) return null;
        if (hex.Length == 0) return string.Empty;

        var s = new StringBuilder();
        foreach (byte b in hex) {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }

    public static byte[] ToHexBytes(this string hex)
    {
        if (hex == null) return null;
        if (hex.Length == 0) return new byte[0];

        int l = hex.Length / 2;
        var b = new byte[l];
        for (int i = 0; i < l; ++i) {
            b[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
        }
        return b;
    }

    public static bool EqualsTo(this byte[] bytes, byte[] bytesToCompare)
    {
        if (bytes == null && bytesToCompare == null) return true; // ?
        if (bytes == null || bytesToCompare == null) return false;
        if (object.ReferenceEquals(bytes, bytesToCompare)) return true;

        if (bytes.Length != bytesToCompare.Length) return false;

        for (int i = 0; i < bytes.Length; ++i) {
            if (bytes[i] != bytesToCompare[i]) return false;
        }
        return true;
    }

}

P
P.K.

还有一个类 UnicodeEncoding,用法很简单:

ByteConverter = new UnicodeEncoding();
string stringDataForEncoding = "My Secret Data!";
byte[] dataEncoded = ByteConverter.GetBytes(stringDataForEncoding);

Console.WriteLine("Data after decoding: {0}", ByteConverter.GetString(dataEncoded));

但不是 UTF-8 吗?
UnicodeEncoding 是有史以来最糟糕的类名; unicode 根本不是编码。该类实际上是 UTF-16。我认为是小端版本。
P
Peter Mortensen

除了选择的答案之外,如果您使用的是 .NET 3.5 或 .NET 3.5 CE,则必须指定要解码的第一个字节的索引,以及要解码的字节数:

string result = System.Text.Encoding.UTF8.GetString(byteArray, 0, byteArray.Length);

这给了我钻石,因为它有效Convert.ToBase64String
F
Fehr

或者:

 var byteStr = Convert.ToBase64String(bytes);

P
Peter Mortensen

BitConverter 类可用于将 byte[] 转换为 string

var convertedString = BitConverter.ToString(byteAttay);

BitConverter 类的文档可以在 MSDN 上找到。


这会将字节数组转换为表示每个字节的十六进制字符串,这在将字节转换为字符串时通常不是您想要的。如果您这样做,那么这是另一个问题,请参见例如 How do you convert Byte Array to Hexadecimal String, and vice versa?
不是OP问的
A
Assimilater

据我所知,给出的答案都不能保证空终止的正确行为。在有人以不同的方式向我展示之前,我编写了自己的静态类来使用以下方法处理此问题:

// Mimics the functionality of strlen() in c/c++
// Needed because niether StringBuilder or Encoding.*.GetString() handle \0 well
static int StringLength(byte[] buffer, int startIndex = 0)
{
    int strlen = 0;
    while
    (
        (startIndex + strlen + 1) < buffer.Length // Make sure incrementing won't break any bounds
        && buffer[startIndex + strlen] != 0       // The typical null terimation check
    )
    {
        ++strlen;
    }
    return strlen;
}

// This is messy, but I haven't found a built-in way in c# that guarentees null termination
public static string ParseBytes(byte[] buffer, out int strlen, int startIndex = 0)
{
    strlen = StringLength(buffer, startIndex);
    byte[] c_str = new byte[strlen];
    Array.Copy(buffer, startIndex, c_str, 0, strlen);
    return Encoding.UTF8.GetString(c_str);
}

startIndex 的原因是在我正在处理的示例中,我特别需要将 byte[] 解析为空终止字符串的数组。在简单的情况下可以安全地忽略它


实际上,我的确实如此。 byteArr.TakeWhile(x => x != 0) 是解决空终止问题的一种快速简便的方法。
“空终止”是什么意思?输入数组中的空字节?您能否准确定义答案中的含义? (但没有“编辑:”、“更新:”或类似的 - 答案应该看起来好像是今天写的。)
我觉得没有必要编辑答案。在将字节数组用于 ascii 编码字符串的低级系统中,数组本身不包含有关字符串长度的信息。最常见的做法是用值 0(又名 null)终止字符串。不这样做是著名的缓冲区溢出漏洞利用的原因。至于这个答案,我几年没用过 c#,所以我不记得它是否只是没有复制空字节或停止复制,直到并包括空字节。但简而言之,这就是空终止
我想也许当它在没有这段代码的情况下继续复制空终止符时......但我又不记得了
P
Peter Mortensen

用于将从文件读取的字节数组 byteArrFilename 转换为纯 ASCII C 风格的以零结尾的字符串的 LINQ 单线器是这样的:对于读取旧存档格式的文件索引表等内容非常方便。

String filename = new String(byteArrFilename.TakeWhile(x => x != 0)
                              .Select(x => x < 128 ? (Char)x : '?').ToArray());

我在这里使用 '?' 作为任何不是纯 ASCII 的默认字符,但当然可以更改。如果您想确保可以检测到它,只需使用 '\0',因为开头的 TakeWhile 可确保以这种方式构建的字符串不可能包含来自输入源的 '\0' 值。


P
Peter Mortensen

试试这个控制台应用程序:

static void Main(string[] args)
{
    //Encoding _UTF8 = Encoding.UTF8;
    string[] _mainString = { "Hello, World!" };
    Console.WriteLine("Main String: " + _mainString);

    // Convert a string to UTF-8 bytes.
    byte[] _utf8Bytes = Encoding.UTF8.GetBytes(_mainString[0]);

    // Convert UTF-8 bytes to a string.
    string _stringuUnicode = Encoding.UTF8.GetString(_utf8Bytes);
    Console.WriteLine("String Unicode: " + _stringuUnicode);
}

P
Peter Mortensen

这是您不必费心编码的结果。我在我的网络类中使用它并将二进制对象作为字符串发送。

public static byte[] String2ByteArray(string str)
{
    char[] chars = str.ToArray();
    byte[] bytes = new byte[chars.Length * 2];

    for (int i = 0; i < chars.Length; i++)
        Array.Copy(BitConverter.GetBytes(chars[i]), 0, bytes, i * 2, 2);

    return bytes;
}

public static string ByteArray2String(byte[] bytes)
{
    char[] chars = new char[bytes.Length / 2];

    for (int i = 0; i < chars.Length; i++)
        chars[i] = BitConverter.ToChar(bytes, i * 2);

    return new string(chars);
}

没有。但此功能用于我们公司网络中的二进制传输,到目前为止,已对 20TB 进行了重新编码和正确编码。所以对我来说这个功能有效:)
S
S.ATTA.M
string result = ASCIIEncoding.UTF8.GetString(byteArray);

GetStringEncoding 类的静态属性(其中 ASCIIEncoding 是派生类型)。此代码与使用 Encoding.UTF8.GetString 相同,许多其他答案已建议使用此代码。请不要发布重复的答案。 From review