ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

然后我看到以下错误:

参数 1:无法从 'object' 转换为 'byte[]' 'System.Text.Encoding.GetString(byte[])' 的最佳重载方法匹配有一些无效参数

我尝试根据 this 帖子修复代码,但仍然没有成功

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

有什么建议么?

searchResult.Properties["user"][0] 的类型是什么?尝试先将其投射到 byte[]
mshsayem 去了我要去的地方。您是否在 searchResult 上错过了对 (byte[]) 的强制转换?
在我的情况下,我将如何做呢?老实说,我对 C# 语法的了解非常有限。
您需要找出 Properties["user"][0] 是什么类型。如果您确定它是一个字节数组,那么您可以像这样 profile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
事实证明,没有必要大惊小怪。毕竟可以在不编码的情况下获取用户名。

T
Timothy Randall

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

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

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

您需要将其转回这样的字符串:

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

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


Timothy,我查看了 VB 代码,但我似乎找不到你提到的字节数组。
在您的搜索结果中,Properties 属性的类型是什么?
我所看到的只是有一些作为字符串附加到属性的项目。我不确定这是否是你问我的。
@AndiAR 尝试 Encoding.UTF8.GetBytes(somestring)
对于我的情况,我发现 Encoding.Unicode.GetBytes 有效(但 ASCII 没有)
S
Shridhar

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

using System.Text;

然后使用此代码

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

希望修复它!


J
Jan Turoň

不应使用 Encoding.Default...

一些答案使用 Encoding.Default,但 Microsoft 提出 a warning against it

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

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

...应该改用UTF-8/UTF-16LE编码...

Encoding.ASCII 得分最高的答案是 7 位,所以它也不起作用,就我而言:

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

按照微软的建议:

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(请参阅@OwnagesMagic 注释),但未将其定义为默认值:许多类,如 StreamWriter默认使用 UTF-8。

...但它并不总是使用

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

让我分享一下我的真实经历:我创建了 WinForms 应用程序,为教师定制了 git 脚本。输出是由进程 described by Microsoft 在后台异步获取的(我添加的粗体文本):

在这种情况下,“shell”一词 (UseShellExecute) 指的是图形 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 这样的许多命令也是本地化的,而其他方法 differs from version to version:在这种环境下可靠地显示国家字符几乎是不可行的。

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


实际上 .Net 和 Windows 在内部使用 UTF-16 处理字符串。 Win32 API 还可以接受以活动代码页 (ACP) 编码的字符串,并将其转换为 UTF-16。 OEM 代码页仅用于控制台 I/O。
@OwnageIsMagic UTF-16LE 有时在内部用于字符串,但 .NET 接口默认使用 UTF-8,我在答案中添加了关于 Encoding.Unicode 的注释。
一些事实检查怎么样? github.com/dotnet/runtime/blob/… 它使用 WCHAR 类型,表示每个字符 16 位 (UTF-16)。 C# 中的 sizeof(char) 也是 2
我很确定即使是 CLR 规范也强制使用 UTF16 进行 System.String 编码。并且不管源文件的编码:它完全无关紧要。编译器在编译期间将源文件编码转换为 UTF16。
您可以使用 docs.microsoft.com/en-us/dotnet/csharp/language-reference/… -codepage 标志为 Roslyn 编译器指定源文件编码。 The compiler will first attempt to interpret all source files as UTF-8. If your source code files are in an encoding other than UTF-8 and use characters other than 7-bit ASCII characters, use the CodePage option to specify which code page should be used.
K
Kuganrajh Rajendran
var result = System.Text.Encoding.Unicode.GetBytes(text);

这应该是公认的答案,因为其他答案建议使用 ASCII,但编码是 Unicode(它是 UTF16)或 UTF8。
确实,@Abel。 C# 目前默认使用 UTF-16,这样的编码比 ASCII 更有意义。当然取决于项目,但这是默认设置。
C
Cristian Ciupitu

您还可以使用 Extension Method 将方法添加到 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();

我将重命名该方法以包括它使用 ASCII 编码的事实。 ToASCIIByteArray 之类的东西。当我发现我正在使用的某个库使用 ASCII 并且我假设它使用 UTF-8 或更现代的东西时,我讨厌它。
J
JustinStolle
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 将有一个字节数组,该数组在每个代理对末尾丢失一个正常字符。 GetString 最后将有空字符。唯一可行的方法是微软的默认值是 UTF32,或者代理对范围内的字符是不允许的。还是有什么我没看到的?正确的方法是将字符串“编码”为字节。
正确,对于更广泛的范围,您可以使用类似于#Timothy Randall 的解决方案的东西: using System;使用 System.Text;命名空间示例{公共类程序{公共静态无效主(字符串[]参数){字符串s1 =“Hello World”;字符串 s2 = "שלום עולם"; string 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))); } } }
@EranYogev 为什么它会失败?我已经在 System.Int32 的整个范围内对其进行了测试,结果是正确的。您能否在此处或在此问题中进行解释:stackoverflow.com/questions/64077979/…
k
knocte

这对我有用

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

反过来:

string str = Convert.ToBase64String(bytes);

仅当您的字符串仅包含 az、AZ、0-9、+、/ 时才有效。不允许使用其他字符 de.wikipedia.org/wiki/Base64
该问题与具有唯一字符限制的 Base64 字符串无关。
D
Dan Sinclair

Ali's answer 的基础上,我会推荐一种扩展方法,允许您有选择地传入要使用的编码:

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

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

用这个

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

N
Noam M

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

保持简单


charstring 根据定义是 UTF-16。
是的,默认值为 UTF-16。我没有对输入字符串的编码做出任何假设。
没有文本,只有编码文本。您的输入是 string 类型,因此是 UTF-16。 UTF-16 不是默认值;没有选择。然后拆分为 char[],UTF-16 代码单元。然后调用 Convert.ToByte(Char),它恰好将 U+0000 转换为 U+00FF 到 ISO-8859-1,并修改任何其他代码点。
说得通。感谢您的澄清。更新我的答案。
我认为您仍然缺少几个要点。专注于 char 为 16 位,Convert.ToByte() 将其中的一半丢弃。
P
Pawel Maga

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

A
Ali

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

A
Algemist

这个问题已经回答了很多次了,但是随着 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(...) 的指针更快。


s
shA.t

有谁知道为什么不这样做?

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

Convert.ToByte(char) 不像您想象的那样工作。字符 '2' 转换为字节 2,而不是表示字符 '2' 的字节。请改用 mystring.Select(x => (byte)x).ToArray()
J
Janus

如果 '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 () )

u
user10863293

这对我有用,之后我可以将我的图片转换为我数据库中的 bytea 字段。

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

i
inno

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

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

该问题与具有唯一字符限制的 Base64 字符串无关。
u
user16863142

谢谢帕维尔·马加

你的贡献可以这样完成:

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

你确定在正确的地方吗?为什么不发表评论而不是发布一个只是为另一个答案增加精确度的新答案?
请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
这以简洁明了的方式给出了最好的答案。可能在另一个答案中,但在那个答案中理论太多。