Chuyển đổi chuỗi thành mảng byte trong C #


668

Tôi đang chuyển đổi một cái gì đó từ VB sang C #. Có vấn đề với cú pháp của tuyên bố này:

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

Sau đó tôi thấy các lỗi sau:

Đối số 1: không thể chuyển đổi từ 'đối tượng' thành 'byte []'

Phương thức được nạp chồng tốt nhất phù hợp với 'System.Text.Encoding.GetString (byte [])' có một số đối số không hợp lệ

Tôi đã cố gắng sửa mã dựa trên bài đăng này , nhưng vẫn không thành công

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

Bất kỳ đề xuất?


1
Các loại là searchResult.Properties["user"][0]gì? Hãy thử truyền nó lên byte[]đầu tiên
mshsayem

mshsayem đã đi nơi tôi đang đi. Bạn có đang thiếu một diễn viên (byte[])trên searchResult không?
Harrison

2
Bạn cần tìm hiểu loại nào Properties["user"][0]. Nếu bạn chắc chắn đó là một mảng byte thì bạn có thể truyền như thế nàyprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
bàn

1
Hóa ra không cần thiết cho tất cả sự ồn ào đó. Tên người dùng có thể được tìm nạp mà không cần mã hóa.
hoạt động vào

3
Tại sao bạn không chọn câu trả lời đúng?
Ali

Câu trả lời:


1182

Nếu bạn đã có một mảng byte thì bạn sẽ cần phải biết loại mã hóa nào đã được sử dụng để biến nó thành mảng byte đó.

Ví dụ: nếu mảng byte được tạo như thế này:

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

Bạn sẽ cần phải biến nó trở lại thành một chuỗi như thế này:

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

Nếu bạn có thể tìm thấy trong mã bạn đã kế thừa, mã hóa được sử dụng để tạo mảng byte thì bạn nên đặt.


3
Timothy, tôi đã xem qua mã VB và dường như tôi không thể tìm thấy một mảng byte như bạn đã đề cập.
nouptime

Trên kết quả tìm kiếm của bạn, loại thuộc tính Thuộc tính là gì?
Timothy Randall

Tất cả những gì tôi có thể thấy là có một số mục được gắn vào Thuộc tính dưới dạng chuỗi. Tôi không chắc đó là những gì bạn đã hỏi tôi mặc dù.
nouptime

16
@AndiAR hãy thử Encoding.UTF8.GetBytes (thuần hóa)
OzBob

1
Đối với tình huống của tôi, tôi thấy rằng Encoding.Unicode.GetBytes đã hoạt động (nhưng ASCII thì không)
Jeff

106

Trước hết, thêm System.Textkhông gian tên

using System.Text;

Sau đó sử dụng mã này

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

Hy vọng sẽ sửa nó!


42

Ngoài ra, bạn có thể sử dụng Phương thức tiện ích mở rộng để thêm phương thức vào stringloại như dưới đây:

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

Và sử dụng nó như dưới đây:

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

12
Tôi đã đổi tên phương thức đó để bao gồm thực tế là nó sử dụng mã hóa ASCII. Một cái gì đó như ToASCIIByteArray. Tôi ghét khi tôi phát hiện ra một số thư viện tôi đang sử dụng ASCII và tôi cho rằng nó đang sử dụng UTF-8 hoặc một cái gì đó hiện đại hơn.
T Trống

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

3
Đây phải là câu trả lời được chấp nhận, vì các câu trả lời khác đề xuất ASCII, nhưng mã hóa là Unicode (mà nó là UTF16) hoặc UTF8.
Abel

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

Điều này sẽ thất bại đối với các ký tự rơi vào phạm vi cặp thay thế .. GetBytes sẽ có một mảng byte bỏ lỡ một cặp char bình thường trên mỗi cặp thay thế. GetString sẽ có ký tự trống ở cuối. Cách duy nhất nó sẽ hoạt động là nếu mặc định của microsoft là UTF32 hoặc nếu các ký tự trong phạm vi cặp thay thế không được phép. Hoặc có điều gì đó tôi không nhìn thấy? Cách thích hợp là 'mã hóa' chuỗi thành byte.
Gerard ONeill

Chính xác, đối với phạm vi rộng hơn, bạn có thể sử dụng một cái gì đó tương tự như giải pháp của #Timothy Randall: sử dụng Hệ thống; sử dụng System.Text; không gian tên Ví dụ {Chương trình lớp công khai {public static void Main (string [] args) {string s1 = "Hello World"; chuỗi s2 = "של TOUR TOUR"; chuỗi 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))); }}}
Eran Yogev

17

Tại sao Encoding.Default không nên được sử dụng ...

Câu trả lời của @ Randall sử dụng Encoding.Default, tuy nhiên Microsoft đưa ra cảnh báo chống lại nó :

Các máy tính khác nhau có thể sử dụng các bảng mã khác nhau làm mặc định và mã hóa mặc định có thể thay đổi trên một máy tính. Nếu bạn sử dụng mã hóa Mặc định để mã hóa và giải mã dữ liệu được truyền phát giữa các máy tính hoặc được truy xuất tại các thời điểm khác nhau trên cùng một máy tính, nó có thể dịch dữ liệu đó không chính xác. Ngoài ra, mã hóa được trả về bởi thuộc tính Mặc định sử dụng dự phòng phù hợp nhất để ánh xạ các ký tự không được hỗ trợ thành các ký tự được trang mã hỗ trợ. Vì những lý do này, không nên sử dụng mã hóa mặc định. Để đảm bảo rằng các byte được mã hóa được giải mã chính xác, bạn nên sử dụng mã hóa Unicode, chẳng hạn như UTF8Encoding hoặc UnicodeEncoding. Bạn cũng có thể sử dụng giao thức cấp cao hơn để đảm bảo rằng định dạng tương tự được sử dụng để mã hóa và giải mã.

Để kiểm tra mã hóa mặc định là gì, hãy sử dụng Encoding.Default.WindowsCodePage(1250 trong trường hợp của tôi - và thật đáng buồn, không có lớp mã hóa CP1250 được xác định trước, nhưng đối tượng có thể được truy xuất dưới dạng Encoding.GetEncoding(1250)).

Encoding.ASCII là 7 bit, vì vậy, nó cũng không hoạt động, trong trường hợp của tôi:

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

... và tại sao nên sử dụng mã hóa UTF-8 thay thế ...

Mã hóa mặc định là sai lệch: .NET sử dụng UTF-8 ở mọi nơi vì mặc định thực (mã hóa 8 bit trở nên lỗi thời vào cuối thế kỷ 20., kiểm tra tức là Console.OutputEncoding.EncodingName*) để mọi hằng số bạn xác định trong mã đều được mã hóa theo mặc định - vì vậy cái này nên được sử dụng trừ khi nguồn dữ liệu ở dạng mã hóa khác nhau.

* Đây là UTF-8 trong trường hợp của tôi là lời nói dối trực tiếp: chcptừ bảng điều khiển windows (cmd) trả về 852 - và điều này không nên thay đổi, vì các lệnh hệ thống cục bộ (như ping) đã được mã hóa mã hóa này

Theo khuyến nghị của Microsoft:

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

Encoding.UTF8 được đề xuất bởi những người khác là một ví dụ mã hóa UTF-8 và cũng có thể được sử dụng trực tiếp hoặc như

var utf8 = Encoding.UTF8 as UTF8Encoding;

... nhưng nó không được sử dụng luôn

Mã hóa cho mảng byte sẽ "chỉ hoạt động" bằng Unicode ở các nước phương Tây, nhưng ngay khi bạn chuyển chương trình của mình sang một số khu vực ít được hỗ trợ hơn (như ở đây ở Đông Âu), đó là một mớ hỗn độn thực sự: ở Cộng hòa Séc, Windows sử dụng mặc định (vào năm 2020!) MS không chuẩn 852 (còn gọi là Latin-2) cho bảng điều khiển, 1250 là Windows OEM, UTF-8 (65001) là .NET (và các loại khác) mặc định mới và chúng ta nên nhớ rằng một số 8bit phía tây EU dữ liệu vẫn còn trong năm 1252, trong khi tiêu chuẩn phương Tây 8 bit cũ cho Đông Âu là ISO-8859-2 (còn gọi là Latin-2, nhưng KHÔNG cùng Latin-2 với 852). Sử dụng ASCII có nghĩa là văn bản đầy đậu phụ và '?' đây. Vì vậy, cho đến nửa thế kỷ 21, vui lòng đặt UTF-8 một cách rõ ràng .


12

Dựa trên câu trả lời của Ali , tôi muốn giới thiệu một phương pháp mở rộng cho phép bạn tùy ý chuyển mã hóa bạn muốn sử dụng:

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

Và sử dụng nó như dưới đây:

string foo = "bla bla";

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

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

2
Lưu ý rằng việc sử dụng Encoding encoding = Encoding.Defaultkết quả trong lỗi thời gian biên dịch:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell

11

Cách tiếp cận sau đây sẽ chỉ hoạt động nếu ký tự là 1 byte. (Unicode mặc định sẽ không hoạt động vì nó là 2 byte)

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

Giữ cho nó đơn giản


charstringtheo định nghĩa UTF-16.
Tom Blodget

Có, mặc định là UTF-16. Tôi không đưa ra bất kỳ giả định nào về Mã hóa chuỗi đầu vào.
Mandar Sudame 6/03/2016

Không có văn bản nhưng văn bản được mã hóa. Đầu vào của bạn là loại stringvà do đó là UTF-16. UTF-16 không phải là mặc định; không có lựa chọn về nó. Sau đó, bạn chia thành char[]các đơn vị mã UTF-16. Sau đó, bạn gọi Convert.ToByte (Char) , điều này chỉ xảy ra để chuyển đổi U + 0000 thành U + 00FF thành ISO-8859-1 và mang bất kỳ loại tiền mã hóa nào khác.
Tom Blodget

Có ý nghĩa. Cảm ơn bạn đã làm rõ. Cập nhật câu trả lời của tôi.
Mandar Sudame

1
Tôi nghĩ rằng bạn vẫn còn thiếu một số điểm cần thiết. Tập trung vào charviệc 16 bit và Convert.ToByte()ném một nửa trong số chúng đi.
Tom Blodget

10

dùng cái này

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

6

Một sàng lọc để chỉnh sửa của JustinStolle (sử dụng BlockCopy của Eran Yogev).

Giải pháp đề xuất thực sự nhanh hơn so với sử dụng Mã hóa. Vấn đề là nó không hoạt động để mã hóa các mảng byte có độ dài không đồng đều. Như được đưa ra, nó đưa ra một ngoại lệ ngoài giới hạn. Việc tăng độ dài thêm 1 để lại một byte theo sau khi giải mã từ chuỗi.

Đối với tôi, nhu cầu đến khi tôi muốn mã hóa từ DataTableđến JSON. Tôi đang tìm cách mã hóa các trường nhị phân thành các chuỗi và giải mã từ chuỗi trở lại byte[].

Do đó, tôi đã tạo ra hai lớp - một lớp bao bọc giải pháp trên (khi mã hóa từ các chuỗi thì không sao, vì độ dài luôn bằng nhau) và lớp khác xử lý byte[]mã hóa.

Tôi đã giải quyết vấn đề độ dài không đồng đều bằng cách thêm một ký tự cho tôi biết nếu độ dài ban đầu của mảng nhị phân là số lẻ ('1') hoặc chẵn ('0')

Như sau:

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âu hỏi này đã được trả lời đủ nhiều lần, nhưng với C # 7.2 và việc giới thiệu loại Span, có một cách nhanh hơn để làm điều này trong mã không an toàn:

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

Hãy nhớ rằng các byte đại diện cho một chuỗi được mã hóa UTF-16 (được gọi là "Unicode" trong vùng đất C #).

Một số điểm chuẩn nhanh cho thấy các phương thức trên nhanh hơn khoảng 5 lần so với triển khai Encoding.Unicode.GetBytes (...) / GetString (...) cho các chuỗi có kích thước trung bình (30-50 ký tự) và thậm chí nhanh hơn đối với các chuỗi lớn hơn. Các phương thức này dường như cũng nhanh hơn so với sử dụng các con trỏ với Marshal.Copy (..) hoặc Buffer.MemoryCopy (...).


4

Nếu kết quả của, 'searchResult.ProperIES ["user"] [0]', là một chuỗi:

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

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

}

Điểm mấu chốt là chuyển đổi chuỗi thành byte [] có thể được thực hiện bằng LINQ:

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

Và ngược lại:

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

3

Có ai thấy lý do tại sao không làm điều này?

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

10
Convert.ToByte(char)không làm việc như bạn nghĩ nó sẽ làm. Ký tự '2'được chuyển đổi thành byte 2, không phải byte đại diện cho ký tự '2'. Sử dụng mystring.Select(x => (byte)x).ToArray()thay thế.
Jack

3

Đây là những gì làm việc cho tôi

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

chỉ hoạt động khi chuỗi của bạn chỉ chứa az, AZ, 0-9, +, /. Không có nhân vật nào khác được phép de.wikipedia.org/wiki/Base64
Blechdose

2

Bạn có thể sử dụng API MemoryMarshal để thực hiện chuyển đổi rất nhanh và hiệu quả. Stringsẽ hoàn toàn được truyền tới ReadOnlySpan<byte>, như MemoryMarshal.Castchấp nhận Span<byte>hoặc ReadOnlySpan<byte>là một tham số đầu vào.

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

Điểm chuẩn sau đây cho thấy sự khác biệt:

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

Công việc này đối với tôi, sau đó tôi có thể chuyển đổi đưa hình ảnh của mình vào trường bytea trong cơ sở dữ liệu của mình.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.