簡體   English   中英

在 C# 中將字符串轉換為字節數組

[英]Converting string to byte array in 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[])”的最佳重載方法匹配有一些無效參數

我試圖根據這篇文章修復代碼,但仍然沒有成功

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

有什么建議么?

如果您已經有一個字節數組,那么您將需要知道使用什么類型的編碼將其放入該字節數組中。

例如,如果字節數組是這樣創建的:

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

您需要將其轉回這樣的字符串:

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

如果您可以在繼承的代碼中找到用於創建字節數組的編碼,那么您應該設置。

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

using System.Text;

然后使用此代碼

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

希望能修好!

您也可以使用擴展方法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);

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

按照微軟的建議:

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;

...但它並不總是使用

默認編碼具有誤導性:.NET 到處都使用 UTF-8(包括在源代碼中硬編碼的字符串),但 Windows 實際上使用了另外 2 個非 UTF8 非標准默認值: ANSI 代碼頁(用於 .NET 之前的 GUI 應用程序)和OEM 代碼頁(又名 DOS 標准)。 這些因國家/地區而異(例如,Windows 捷克語版使用 CP1250 和 CP852)並且通常在 Windows API 庫中進行硬編碼。 因此,如果您只是通過chcp 65001將 UTF-8 設置為控制台(就像 .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)。

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

這對我有用

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

反過來:

string str = Convert.ToBase64String(bytes);

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

用這個

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

僅當字符為 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;
}

保持簡單

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

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

這個問題已經回答了很多次了,但是隨着 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(...) 的指針更快。

有沒有人看到不這樣做的任何理由?

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

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

在 C# 11 之前

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

在 C# 11 中,您現在可以簡單地將一個 u8 后綴附加到您的字符串文字,以立即將它們轉換為 UTF-8

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

閱讀有關UTF-8 字符串文字的文檔

這對我有用,之后我可以轉換將我的圖片放在我的數據庫中的 bytea 字段中。

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

這已經得到了很多回答,但對我來說,唯一的工作方法是這個:

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

C# 11中,您可以使用UTF-8 字符串文字,這使得它變得超級簡單並且具有更好的性能並且沒有 memory 分配。

byte[] array = "some text";

或者,如果您已經有一個字符串值:

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

這是使用UTF-8 encdoingGetBytes )到C# 11 UTF-8 String Literlas方式( GetBytesNew )的舊方式之間的不同示例。

在此處輸入圖像描述

謝謝帕維爾·馬加

你的貢獻可以這樣完成:

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

從 .Net5 開始,您可以使用Convert.ToHexString 還有一種反向操作的方法: Convert.FromHexString

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM