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

我正在将一些东西从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[])'有一些无效的参数

我试图修复基于这个 post的代码,但仍然没有成功

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

有什么建议吗?

1694716 次浏览

如果你已经有一个字节数组,那么你需要知道使用了什么类型的编码来使它成为该字节数组。

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

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

您需要将其转换回字符串,如下所示:

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

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

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

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

使用此

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);

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

using System.Text;

然后使用此代码

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

希望修复它!

仅当字符为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;}

保持简单

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

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

您还可以使用扩展方法将方法添加到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();
var result = System.Text.Encoding.Unicode.GetBytes(text);

这个问题已经回答了很多次了,但是在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#land中称为“Unicode”)。

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

如果'搜索结果. 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将字符串转换为byte[]:

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

反过来说:

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

这项工作对我来说,之后,我可以转换把我的照片放在我的数据库中的一个字节茶字段。

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[])){return s.ToArray();}

阿里的回答的基础上,我推荐一种扩展方法,允许您选择性地传入要使用的编码:

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 encodingbyte[] default = foo.ToByteArray();
// custom encodingbyte[] unicode = foo.ToByteArray(Encoding.Unicode);

您可以使用应用程序接口来执行非常快速和高效的转换。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 spanspublic 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 |      - |     - |     - |         - |

这对我有用

byte[] bytes = Convert.FromBase64String(textString);

反过来:

string str = Convert.ToBase64String(bytes);

编码。不应使用默认值…

一些答案使用Encoding.Default,但微软提出了对它的警告

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

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

…应使用UTF-8/UTF-16LE编码代替…

Encoding.ASCII在得分最高的答案是7bit,所以它也不起作用,在我的情况下:

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

根据Microsoft的建议:

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

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

var utf8 = Encoding.UTF8 as UTF8Encoding;

Encoding.Unicode在内存中的字符串表示中很流行,因为它每个字符使用固定的2个字节,所以可以在恒定的时间内跳转到第n个字符,而代价是更多的内存使用:它是UTF-16LE。在MSVC#中,*. cs文件默认为UTF-8 BOM,其中的字符串常量在编译时转换为UTF-16LE(参见@OwnagelsMagic注释),但它没有定义为默认值:像StreamWriter这样的许多类默认使用UTF-8。

…但它并不总是被使用

默认编码会产生误导:. NET在任何地方都使用UTF-8(包括在源代码中硬编码的字符串)和UTF-16LE(Encoding.Unicode)来将字符串存储在内存中,但Windows实际上使用了另外两个非UTF8默认值:ANSI代码页(用于. NET之前的GUI应用程序)和OEM代码页(又名DOS标准)。这些默认值因国家/地区而异(例如,Windows捷克版使用CP1250和CP852),并且通常在Windows API库中硬编码。因此,如果您将UTF-8设置为控制台chcp 65001(正如. NET隐式地做并假装它是默认值)并运行一些本地化命令(如ping),它可以在英文版中工作,但您会得到捷克共和国的豆腐文本。

让我分享一下我的真实世界经验:我为教师创建了自定义git脚本的WinForms应用程序。输出由进程由Microsoft描述在后台随时随地获得(我添加的粗体文本):

在这种情况下,单词“shell”指的是图形shell(ANSI CP)(类似于Windows shell)而不是命令shell(例如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或UTF-16LE)。

这已经回答了很多,但对我来说,唯一的工作方法是这个:

    public static byte[] StringToByteArray(string str){byte[] array = Convert.FromBase64String(str);return array;}

谢谢你Pawel Maga

您的贡献可以这样完成:

    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray();public static string FromByteArray(this byte[] bytes) => ToCharSpan(new ReadOnlySpan<byte>(bytes)).ToString();public static ReadOnlySpan<byte> ToByteSpan(this string str) => MemoryMarshal.Cast<char, byte>(str);public static ReadOnlySpan<char> ToCharSpan(this ReadOnlySpan<byte> bytes) => MemoryMarshal.Cast<byte, char>(bytes);

cUTF-8字符串文字1中,您可以使用UTF-8字符串文字,这使得它非常容易并且具有更好的性能并且没有内存分配。

byte[] array = "some text";

或者,如果您已经有一个字符串值:

string input = "some text";byte[] array = input;

这是使用UTF-8 encdoingGetBytes)的旧方式与c#11UTF-8 String Literlas方式(GetBytesNew)之间的不同的示例。

输入图片描述

在C#11之前

ReadOnlySpan<byte> before = System.Text.Encoding.UTF8.GetBytes("hello!");

在C#11中,您现在可以简单地将u8后缀附加到字符串文字中,以便立即以UTF-8格式获取它们

ReadOnlySpan<byte> now = "hello!"u8;

阅读UTF-8字符串文字上的文档