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


669

我正在将某些东西从VB转换为C#。该语句的语法有问题:

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

然后,我看到以下错误:

参数1:无法从“对象”转换为“字节[]”

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

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

string User = Encoding.UTF8.GetString("user", 0);

有什么建议么?


1
什么是searchResult.Properties["user"][0]?尝试byte[]先将其投放
mshsayem

mshsayem去了我要去的地方。您是否缺少(byte[])对searchResult的强制转换?
哈里森

2
您需要找出什么类型Properties["user"][0]。如果您确定它是一个字节数组,则可以像这样进行转换profile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP

1
事实证明,没有必要这么大惊小怪。毕竟,无需编码就可以获取用户名。
nouptime 2014年

3
为什么不选择真实答案?
阿里

Answers:


1186

如果您已经有一个字节数组,那么您将需要知道使用哪种编码类型将其写入该字节数组。

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

byte[] bytes = Encoding.ASCII.GetBytes(someString);

您将需要将其重新变成这样的字符串:

string someString = Encoding.ASCII.GetString(bytes);

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


3
蒂莫西(Timothy),我浏览了VB代码,但似乎找不到如你所提到的字节数组。
nouptime

在您的搜索结果中,Properties属性的类型是什么?
蒂莫西·兰德尔

我所看到的是,有一些数字项以字符串形式附加到“属性”。我不确定那是否就是你在问我的。
nouptime

16
@AndiAR尝试Encoding.UTF8.GetBytes(somestring)
OzBob

1
对于我的情况,我发现Encoding.Unicode.GetBytes可以工作(但是ASCII不起作用)
Jeff

106

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

using System.Text;

然后使用此代码

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);

希望解决它!


42

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

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

并如下所示使用它:

string foo = "bla bla";
byte[] result = foo.ToByteArray();

12
我已将该方法重命名为包含使用ASCII编码的事实。有点像ToASCIIByteArray。我讨厌当我发现某些正在使用的库使用ASCII并且我假设它正在使用UTF-8或更现代的东西时。
T空白

30
var result = System.Text.Encoding.Unicode.GetBytes(text);

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

26
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);
}

对于落入代理对范围内的字符,这将失败。.GetBytes将具有一个字节数组,该字节数组会在末尾每个代理对丢失一个普通的char。GetString将在最后有空字符。唯一可行的方法是,如果Microsoft的默认值是UTF32,或者如果不允许代理对范围内的字符。还是我看不到的东西?正确的方法是将字符串“编码”为字节。
Gerard ONeill

正确,对于更广泛的范围,您可以使用类似于#Timothy Randall解决方案的方法:使用System.Text; 名称空间示例{公共类程序{公共静态void Main(string [] args){string s1 =“ Hello World”; 字符串s2 =“שלוםעולם”; 字符串s3 =“你好,世界!”; Console.WriteLine(Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(s1))); Console.WriteLine(Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(s2))); Console.WriteLine(Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(s3))); }}}
伊兰·约格夫

17

为什么不应该使用Encoding.Default ...

@Randall的答案使用Encoding.Default,但是Microsoft 对此提出警告

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

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

Encoding.ASCII 是7位,因此在我的情况下也不起作用:

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

...以及为何应改用UTF-8编码...

默认编码具有误导性:.NET在任何地方都使用UTF-8作为真正的默认值(20世纪末8位编码已过时,请查看 Console.OutputEncoding.EncodingName *),因此您在代码中定义的每个常量默认都是UTF-8编码的-因此除非数据源使用不同的编码,否则应使用此选项。

*就我而言,这是UTF-8,这是直接的谎言: chcp从Windows控制台(cmd)返回852-并且不应更改此值,因为本地化的系统命令(如ping)已将此代码页进行了硬编码

遵循微软的建议:

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

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

var utf8 = Encoding.UTF8 as UTF8Encoding;

...但是并不总是使用

在西方国家,字节数组的编码在Unicode中应该“可以正常使用”,但是,一旦将程序移到一些不被支持的区域(例如东欧),这确实是一团糟:在捷克共和国,Windows默认使用(在2020年!)用于控制台的MS非标准852(又名Latin-2),Windows OEM为1250,.NET为UTF-8(65001)(以及其他)新的默认设置,我们应该记住,一些西方的欧盟8bit数据仍在1252,而东欧的旧8位西方标准是ISO-8859-2(又名Latin-2,但与852不同的Latin-2)。使用ASCII表示充满豆腐和'?'的文本 这里。因此,直到21世纪上半叶,请明确设置UTF-8 。


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);
    }
}

并如下所示使用它:

string foo = "bla bla";

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

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);

2
请注意,使用Encoding encoding = Encoding.Default结果会导致编译时错误:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell,

11

仅当char为1字节时,以下方法才有效。(因为它是2个字节,所以默认unicode无法正常工作)

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;
}

保持简单


char并且string根据定义为UTF-16。
Tom Blodget

是的,默认值为UTF-16。我没有对输入字符串的编码做任何假设。
Mandar Sudame'3

没有文字,只有编码文字。您输入的是类型string,因此为UTF-16。UTF-16不是默认值;别无选择。然后char[],您分成UTF-16代码单元。然后,您调用Convert.ToByte(Char),恰好是将U + 0000转换为U + 00FF并转换为ISO-8859-1,并处理其他任何代码点。
汤姆·布洛杰特16-3-6

说得通。感谢您的澄清。更新我的答案。
Mandar Sudame

1
我认为您仍然缺少一些要点。专注于char成为16位,Convert.ToByte()并将其中一半丢掉。
汤姆·布洛杰特16-3-9


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;
    }
}

4

这个问题已经被回答了很多次,但是在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));
        }
    }
}

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

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


4

如果'searchResult.Properties [“ user”] [0]'的结果是字符串:

if ( ( searchResult.Properties [ "user" ].Count > 0 ) ) {

   profile.User = System.Text.Encoding.UTF8.GetString ( searchResult.Properties [ "user" ] [ 0 ].ToCharArray ().Select ( character => ( byte ) character ).ToArray () );

}

关键是可以使用LINQ将字符串转换为字节[]。

.ToCharArray ().Select ( character => ( byte ) character ).ToArray () )

反之:

.Select ( character => ( char ) character ).ToArray () )

3

有人看到任何不这样做的理由吗?

mystring.Select(Convert.ToByte).ToArray()

10
Convert.ToByte(char)不能像您想的那样工作。字符'2'将转换为字节2,而不是表示字符的字节'2'。使用mystring.Select(x => (byte)x).ToArray()代替。
杰克


2

您可以使用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);
}

以下基准显示了差异:

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 |      - |     - |     - |         - |

0

这项工作对我而言,之后我可以将图片转换为数据库中的BYtea字段。

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.