在C#中将字符串转换为字节数组

nou*_*ime 594 c# string encoding byte

我对C#很陌生.我正在将VB中的内容转换为C#.遇到此语句的语法问题:

if ((searchResult.Properties["user"].Count > 0))
{
    profile.User = System.Text.Encoding.UTF8.GetString(searchResult.Properties["user"][0]);
}
Run Code Online (Sandbox Code Playgroud)

然后我看到以下错误:

参数1:无法从'object'转换为'byte []'

'System.Text.Encoding.GetString(byte [])'的最佳重载方法匹配有一些无效的参数

我试图根据这篇文章修复代码,但仍然没有成功

string User = Encoding.UTF8.GetString("user", 0);
Run Code Online (Sandbox Code Playgroud)

有什么建议?

Tim*_*all 1032

如果您已经有一个字节数组,那么您将需要知道使用什么类型的编码进入该字节数组.

例如,如果字节数组是这样创建的:

byte[] bytes = Encoding.ASCII.GetBytes(someString);
Run Code Online (Sandbox Code Playgroud)

您需要将其重新转换为如下字符串:

string someString = Encoding.ASCII.GetString(bytes);
Run Code Online (Sandbox Code Playgroud)

如果您可以在继承的代码中找到用于创建字节数组的编码,则应该设置.

  • @AndiAR尝试Encoding.UTF8.GetBytes(somestring) (13认同)
  • Timothy,我查看了VB代码,我似乎无法找到你提到的字节数组. (2认同)

Shr*_*har 98

首先,添加System.Text命名空间

using System.Text;
Run Code Online (Sandbox Code Playgroud)

然后使用此代码

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);
Run Code Online (Sandbox Code Playgroud)

希望能解决它!


Jan*_*roň 43

Encoding.Default 不应使用...

@Randall 的回答使用了Encoding.Default,但微软对它提出了警告

不同的计算机可以使用不同的编码作为默认编码,并且默认编码可以在单台计算机上更改。如果您使用默认编码对计算机之间流式传输的数据或在同一台计算机上不同时间检索的数据进行编码和解码,则可能会错误地转换该数据。此外,由 Default 属性返回的编码使用最佳回退将不支持的字符映射到代码页支持的字符。由于这些原因,不建议使用默认编码。为确保正确解码编码字节,您应该使用 Unicode 编码,例如 UTF8Encoding 或 UnicodeEncoding。您还可以使用更高级别的协议来确保使用相同的格式进行编码和解码。

要检查默认编码是什么,请使用Encoding.Default.WindowsCodePage(在我的情况下为 1250 - 遗憾的是,没有预定义的 CP1250 编码类,但可以将对象检索为Encoding.GetEncoding(1250))。

...应该使用UTF-8编码来代替...

Encoding.ASCII 是 7 位,所以它也不起作用,就我而言:

byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e
Run Code Online (Sandbox Code Playgroud)

按照微软的建议:

var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže
Run Code Online (Sandbox Code Playgroud)

Encoding.UTF8 别人推荐的是uf UTF-8编码的实例,也可以直接使用,也可以作为

var utf8 = Encoding.UTF8 as UTF8Encoding;
Run Code Online (Sandbox Code Playgroud)

...但它并不总是使用

默认编码具有误导性:.NET 到处使用 UTF-8(包括在源代码中硬编码的字符串),但 Windows 实际上使用另外 2 个非 UTF8 非标准默认值:ANSI 代码页(用于 .NET 之前的 GUI 应用程序)和OEM 代码页(又名 DOS 标准)。这些因国家/地区而异(例如,Windows 捷克语版使用 CP1250 和 CP852)并且通常在 Windows API 库中进行硬编码。因此,如果您只是将 UTF-8 设置为控制台chcp 65001(就像 .NET 隐式那样并假装它是默认值)并运行一些本地化命令(如 ping),它可以在英文版本中运行,但您会在捷克共和国获得豆腐文本。

让我分享我的真实经历:我创建了 WinForms 应用程序,为教师自定义 git 脚本。输出是通过 Microsoft 描述的过程在后台同步获得的(我添加的粗体文本):

此上下文中的“外壳”一词(UseShellExecute)是指图形外壳(ANSI CP)(类似于 Windows 外壳)而不是命令外壳(例如 bash 或 sh)(OEM CP)并允许用户启动图形应用程序或打开文档(在非美国环境中输出混乱)

如此有效的 GUI 默认为 UTF-8,进程默认为 CP1250,控制台默认为 852。所以输出在 852 中解释为 UTF-8,解释为 CP1250。我得到了豆腐文本,由于双重转换,我无法从中推断出原始代码页。我花了一个星期的时间来弄清楚为进程脚本明确设置 UTF-8 并将输出从 CP1250 转换为主线程中的 UTF-8。现在它在东欧工作,但西欧 Windows 使用 1252。ANSI CP 不容易确定,因为许多命令systeminfo也已本地化,其他方法因版本而异:在这种环境中,可靠地显示国家字符几乎是不可行的。

因此,直到 21 世纪下半叶,请不要使用任何“默认代码页”明确设置(如果可能,请设置为 UTF-8)。


Ali*_*Ali 38

您还可以使用扩展方法向该string类型添加方法,如下所示:

static class Helper
{
   public static byte[] ToByteArray(this string str)
   {
      return System.Text.Encoding.ASCII.GetBytes(str);
   }
}
Run Code Online (Sandbox Code Playgroud)

并使用如下:

string foo = "bla bla";
byte[] result = foo.ToByteArray();
Run Code Online (Sandbox Code Playgroud)

  • 我重命名该方法以包括它使用ASCII编码的事实.像'ToASCIIByteArray`这样的东西.我讨厌当我找到一些我正在使用的库时使用ASCII而我假设它使用的是UTF-8或更现代的东西. (9认同)

Era*_*gev 24

static byte[] GetBytes(string str)
{
     byte[] bytes = new byte[str.Length * sizeof(char)];
     System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
     return bytes;
}

static string GetString(byte[] bytes)
{
     char[] chars = new char[bytes.Length / sizeof(char)];
     System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
     return new string(chars);
}
Run Code Online (Sandbox Code Playgroud)


小智 22

var result = System.Text.Encoding.Unicode.GetBytes(text);
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案,因为其他答案都建议使用ASCII,但是编码方式是Unicode(它为UTF16)或UTF8。 (2认同)

Min*_*tta 13

这对我有用

byte[] bytes = Convert.FromBase64String(textString);
Run Code Online (Sandbox Code Playgroud)

反过来:

string str = Convert.ToBase64String(bytes);
Run Code Online (Sandbox Code Playgroud)

  • 仅当您的字符串仅包含 az、AZ、0-9、+、/ 时才有效。不允许使用其他字符 https://de.wikipedia.org/wiki/Base64 (3认同)

Dan*_*air 12

基于Ali的答案,我建议一种扩展方法,该方法允许您有选择地传递要使用的编码:

using System.Text;
public static class StringExtensions
{
    /// <summary>
    /// Creates a byte array from the string, using the 
    /// System.Text.Encoding.Default encoding unless another is specified.
    /// </summary>
    public static byte[] ToByteArray(this string str, Encoding encoding = Encoding.Default)
    {
        return encoding.GetBytes(str);
    }
}
Run Code Online (Sandbox Code Playgroud)

并如下所示使用它:

string foo = "bla bla";

// default encoding
byte[] default = foo.ToByteArray();

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,使用 `Encoding encoding = Encoding.Default` 会导致编译时错误:`CS1736 Default parameter value for 'encoding' must be a compile-time constant` (4认同)

ali*_*ini 11

用这个

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
Run Code Online (Sandbox Code Playgroud)


小智 10

仅当字符为1个字节时,以下方法才有效.(默认unicode不起作用,因为它是2个字节)

public static byte[] ToByteArray(string value)
{            
    char[] charArr = value.ToCharArray();
    byte[] bytes = new byte[charArr.Length];
    for (int i = 0; i < charArr.Length; i++)
    {
        byte current = Convert.ToByte(charArr[i]);
        bytes[i] = current;
    }

    return bytes;
}
Run Code Online (Sandbox Code Playgroud)

保持简单


Rom*_*syk 7

C# 11 之前

ReadOnlySpan<byte> before = System.Text.Encoding.UTF8.GetBytes("hello!");
Run Code Online (Sandbox Code Playgroud)

在 C# 11 中,您现在只需将 u8 后缀附加到字符串文字即可立即将其转换为 UTF-8

ReadOnlySpan<byte> now = "hello!"u8;
Run Code Online (Sandbox Code Playgroud)

阅读有关UTF-8 字符串文字的文档


小智 6

对JustinStolle编辑的改进(Eran Yogev使用BlockCopy).

建议的解决方案确实比使用编码更快.问题是它不适用于编码长度不均匀的字节数组.如上所述,它引发了一个越界异常.将长度增加1会在从字符串解码时留下尾随字节.

对我来说,当我想编码时DataTable需要JSON.我正在寻找一种方法将二进制字段编码为字符串并从字符串解码回byte[].

因此,我创建了两个类 - 一个包含上述解决方案(当从字符串编码时很好,因为长度总是均匀),另一个处理byte[]编码.

我通过添加一个字符来解决不均匀长度问题,该字符告诉我二进制数组的原始长度是奇数('1')还是偶数('0')

如下:

public static class StringEncoder
{
    static byte[] EncodeToBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
    static string DecodeToString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

public static class BytesEncoder
{
    public static string EncodeToString(byte[] bytes)
    {
        bool even = (bytes.Length % 2 == 0);
        char[] chars = new char[1 + bytes.Length / sizeof(char) + (even ? 0 : 1)];
        chars[0] = (even ? '0' : '1');
        System.Buffer.BlockCopy(bytes, 0, chars, 2, bytes.Length);

        return new string(chars);
    }
    public static byte[] DecodeToBytes(string str)
    {
        bool even = str[0] == '0';
        byte[] bytes = new byte[(str.Length - 1) * sizeof(char) + (even ? 0 : -1)];
        char[] chars = str.ToCharArray();
        System.Buffer.BlockCopy(chars, 2, bytes, 0, bytes.Length);

        return bytes;
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这个问题已经回答了很多次了,但是随着 C# 7.2 和 Span 类型的引入,有一种更快的方法可以在不安全的代码中做到这一点:

public static class StringSupport
{
    private static readonly int _charSize = sizeof(char);

    public static unsafe byte[] GetBytes(string str)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (str.Length == 0) return new byte[0];

        fixed (char* p = str)
        {
            return new Span<byte>(p, str.Length * _charSize).ToArray();
        }
    }

    public static unsafe string GetString(byte[] bytes)
    {
        if (bytes == null) throw new ArgumentNullException(nameof(bytes));
        if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
        if (bytes.Length == 0) return string.Empty;

        fixed (byte* p = bytes)
        {
            return new string(new Span<char>(p, bytes.Length / _charSize));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请记住,字节表示 UTF-16 编码的字符串(在 C# 中称为“Unicode”)。

一些快速基准测试表明,对于中等大小的字符串(30-50 个字符),上述方法比它们的 Encoding.Unicode.GetBytes(...)/GetString(...) 实现快 5 倍,对于较大的字符串甚至更快。这些方法似乎也比使用 Marshal.Copy(..) 或 Buffer.MemoryCopy(...) 的指针更快。


Paw*_*aga 5

您可以使用MemoryMarshal API来执行非常快速和高效的转换。String将隐式转换为ReadOnlySpan<byte>,作为MemoryMarshal.Cast接受Span<byte>ReadOnlySpan<byte>作为输入参数。

public static class StringExtensions
{
    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray(); //  heap allocation, use only when you cannot operate on spans
    public static ReadOnlySpan<byte> ToByteSpan(this string s) => MemoryMarshal.Cast<char, byte>(s);
}
Run Code Online (Sandbox Code Playgroud)

以下基准显示了差异:

Input: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"

|                       Method |       Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------- |-----------:|----------:|----------:|-------:|------:|------:|----------:|
| UsingEncodingUnicodeGetBytes | 160.042 ns | 3.2864 ns | 6.4099 ns | 0.0780 |     - |     - |     328 B |
| UsingMemoryMarshalAndToArray |  31.977 ns | 0.7177 ns | 1.5753 ns | 0.0781 |     - |     - |     328 B |
|           UsingMemoryMarshal |   1.027 ns | 0.0565 ns | 0.1630 ns |      - |     - |     - |         - |
Run Code Online (Sandbox Code Playgroud)