كيف يمكنني الحصول على تمثيل البايت المتناسق للسلاسل في C#بدون تحديد الترميز يدوياً؟



string.format c# (20)

كما يرجى توضيح سبب مراعاة الترميز. لا يمكنني ببساطة الحصول على ما بايت تم تخزين السلسلة في؟ لماذا هذا الاعتماد على الترميز؟

لأنه لا يوجد شيء اسمه "بايت السلسلة".

تتألف السلسلة (أو أكثر ، نصًا) من الأحرف: الحروف والأرقام والرموز الأخرى. هذا كل شئ. أجهزة الكمبيوتر ، ومع ذلك ، لا تعرف أي شيء عن الشخصيات. يمكنهم فقط التعامل مع وحدات البايت. لذلك ، إذا كنت تريد تخزين أو إرسال نص باستخدام جهاز كمبيوتر ، فستحتاج إلى تحويل الأحرف إلى وحدات البايت. كيف تفعل ذلك؟ هنا حيث تأتي الترميزات إلى مكان الحادث.

الترميز ليس سوى اصطلاح لترجمة أحرف منطقية إلى وحدات البايت الفعلية. أبسط وأشهر التشفير هو ASCII ، وكل ما تحتاج إليه هو الكتابة باللغة الإنجليزية. أما بالنسبة للغات الأخرى ، فستحتاج إلى المزيد من الترميزات الكاملة ، كونها أي من نكهات Unicode هي الخيار الأكثر أمانًا في الوقت الحاضر.

لذا ، باختصار ، فإن محاولة الحصول على "بايت من سلسلة بدون استخدام الترميزات" مستحيل مثل "كتابة نص دون استخدام أي لغة".

بالمناسبة ، أوصيك بشدة (وأي شخص ، لهذه المسألة) بقراءة هذه الحكمة الصغيرة: joelonsoftware.com/articles/Unicode.html

https://code.i-harness.com

كيف يمكنني تحويل string إلى byte[] في .NET (C #) دون تحديد ترميز معين يدويًا؟

سأقوم بتشفير السلسلة. يمكنني تشفيرها دون تحويل ، ولكنني لا زلت أرغب في معرفة سبب تشغيل التشفير هنا.

أيضا ، لماذا ينبغي أن تؤخذ الترميز في الاعتبار؟ لا يمكنني ببساطة الحصول على ما بايت تم تخزين السلسلة في؟ لماذا هناك اعتماد على ترميزات الشخصيات؟


It depends on what you want the bytes FOR

This is because, as Tyler so aptly said , "Strings aren't pure data. They also have information ." In this case, the information is an encoding that was assumed when the string was created.

Assuming that you have binary data (rather than text) stored in a string

This is based off of OP's comment on his own question, and is the correct question if I understand OP's hints at the use-case.

Storing binary data in strings is probably the wrong approach because of the assumed encoding mentioned above! Whatever program or library stored that binary data in a string (instead of a byte[] array which would have been more appropriate) has already lost the battle before it has begun. If they are sending the bytes to you in a REST request/response or anything that must transmit strings, Base64 would be the right approach.

If you have a text string with an unknown encoding

Everybody else answered this incorrect question incorrectly.

If the string looks good as-is, just pick an encoding (preferably one starting with UTF), use the corresponding System.Text.Encoding.???.GetBytes() function, and tell whoever you give the bytes to which encoding you picked.


C # لتحويل string إلى صفيف byte :

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}

استعمال:

    string text = "string";
    byte[] array = System.Text.Encoding.UTF8.GetBytes(text);

The result is:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103

الجزء الأول من سؤالك (كيفية الحصول على وحدات البايت) تمت الإجابة عليه بالفعل من قبل الآخرين: ابحث في مساحة الاسم System.Text.Encoding .

سوف أتناول سؤال المتابعة الخاص بك: لماذا تحتاج إلى اختيار الترميز؟ لماذا لا يمكنك الحصول على ذلك من فئة السلسلة نفسها؟

الجواب في جزأين.

أولاً وقبل كل شيء ، لا يهم استخدام وحدات البايت المستخدمة داخليًا في فئة السلسلة ، وكلما افترضت أنها لا تفعل ذلك ، من المحتمل أنك تقدم خطأً.

إذا كان برنامجك بالكامل داخل .Net world فلا داعي للقلق بشأن الحصول على صفائف بايت للسلاسل على الإطلاق ، حتى إذا كنت ترسل البيانات عبر شبكة. بدلاً من ذلك ، استخدم .Net Serialization للقلق بشأن نقل البيانات. لا تقلق بشأن البايتات الفعلية أكثر من ذلك: تنسيق المنسق يفعل ذلك نيابة عنك.

من ناحية أخرى ، ماذا لو كنت ترسل هذه البايتات في مكان ما لا يمكنك ضمانه ، ستسحب البيانات من تدفق .Net المتسلسل؟ في هذه الحالة ، لا داعي للقلق بشأن الترميز ، لأنه من الواضح أن هذا النظام الخارجي يهتم به. لذا ، مرة أخرى ، لا يهم البايتات الداخلية المستخدمة من قبل السلسلة: تحتاج إلى اختيار تشفير بحيث يمكنك أن تكون صريحًا حول هذا الترميز على الطرف المستقبل ، حتى لو كان نفس الترميز المستخدم داخليًا بواسطة .Net.

أتفهم أنه في هذه الحالة ، قد تفضل استخدام وحدات البايت الفعلية المخزنة بواسطة متغير السلسلة في الذاكرة حيثما أمكن ، مع فكرة أنه قد يحفظ بعض العمل في إنشاء تدفق البايت الخاص بك. ومع ذلك ، فإنني أضعها لك ، إنها غير مهمة مقارنة بالتأكد من أن ناتجك مفهوما في الطرف الآخر ، ولضمان أن تكون صريحا مع الترميز الخاص بك. بالإضافة إلى ذلك ، إذا كنت تريد بالفعل مطابقة وحدات البايت الداخلية ، يمكنك بالفعل اختيار ترميز Unicode والحصول على وفورات الأداء.

الذي يقودني إلى الجزء الثاني ... اختيار ترميز Unicode يخبر. صافي لاستخدام وحدات البايت الأساسية. أنت بحاجة إلى اختيار هذا الترميز ، لأنه عندما يخرج Unicode-Plus متضررًا جديدًا ، يجب أن يكون وقت تشغيل Net. مجانيًا لاستخدام هذا النموذج الجديد الأفضل للترميز دون كسر البرنامج. ولكن ، في الوقت الحالي (والمستقبل القريب) ، فإن اختيار ترميز Unicode يمنحك ما تريده.

من المهم أيضًا أن يتم إعادة كتابة السلسلة الخاصة بك إلى السلك ، وهذا ينطوي على الأقل على ترجمة بعض أنماط البتات حتى عند استخدام تشفير مطابق . يحتاج الكمبيوتر إلى حساب أشياء مثل Big vs Little Endian ، ترتيب بايت الشبكة ، الحزم ، معلومات الجلسة ، إلخ.


تحتاج إلى أخذ الترميز بعين الاعتبار ، لأنه يمكن تمثيل حرف واحد بمقدار 1 بايت أو أكثر (حتى حوالي 6) ، وسوف تتعامل الترميزات المختلفة مع هذه البايتات بشكل مختلف.

جويل لديه نشر على هذا:

joelonsoftware.com/articles/Unicode.html


حسنا ، لقد قرأت جميع الإجابات وكانوا حول استخدام الترميز أو واحد عن التسلسل الذي يسقط بديلين غير مزايدات.

انها سيئة عندما تأتي السلسلة ، على سبيل المثال ، من SQL Server حيث تم بناءه من صفيف تخزين البايت ، على سبيل المثال ، تجزئة كلمة المرور. إذا قمنا بإسقاط أي شيء منه ، فسوف يقوم بتخزين تجزئة غير صالحة ، وإذا أردنا تخزينها في XML ، فنحن نريد أن نتركها سليمة (لأن كاتب XML يسقط استثناءً عن أي بديل غير مزاوج يجده).

لذلك أستخدم ترميز Base64 لصفائف البايتات في مثل هذه الحالات ، ولكن يا للهول ، يوجد على الإنترنت حل واحد لهذا في C # ، ولديه خلل فيه وهو طريقة واحدة فقط ، لذلك أصلحت الخطأ وأعدت كتابتي إجراء. أنت هنا ، موظفو Google المستقبليون:

public static byte[] StringToBytes(string str)
{
    byte[] data = new byte[str.Length * 2];
    for (int i = 0; i < str.Length; ++i)
    {
        char ch = str[i];
        data[i * 2] = (byte)(ch & 0xFF);
        data[i * 2 + 1] = (byte)((ch & 0xFF00) >> 8);
    }

    return data;
}

public static string StringFromBytes(byte[] arr)
{
    char[] ch = new char[arr.Length / 2];
    for (int i = 0; i < ch.Length; ++i)
    {
        ch[i] = (char)((int)arr[i * 2] + (((int)arr[i * 2 + 1]) << 8));
    }
    return new String(ch);
}

فقط لإثبات أن إجابة Mehrdrad السليمة تعمل ، يمكن لمقاربته أن تستمر حتى في [BinaryFormatter (التي كان العديد منها قد وجهت ضد إجابتي ، ولكن الجميع متساوون في ذلك ، على سبيل المثال System.Text.Encoding.UTF8.GetBytes ، System.Text.Encoding.Unicode.GetBytes ؛ لا يمكن لأساليب الترميز هذه أن تستمر على الأحرف البديلة الكبيرة d800 على سبيل المثال ، وتلك فقط مجرد استبدال أحرف بديلة عالية مع القيمة fffd ):

using System;

class Program
{     
    static void Main(string[] args)
    {
        string t = "爱虫";            
        string s = "Test\ud800Test"; 

        byte[] dumpToBytes = GetBytes(s);
        string getItBack = GetString(dumpToBytes);

        foreach (char item in getItBack)
        {
            Console.WriteLine("{0} {1}", item, ((ushort)item).ToString("x"));
        }    
    }

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

انتاج:

T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

حاول ذلك مع System.Text.Encoding.UTF8.GetBytes أو System.Text.Encoding.Unicode.GetBytes ، فإنها سوف تستبدل فقط أحرف بديلة عالية مع القيمة fffd

في كل مرة يكون هناك حركة في هذا السؤال ، ما زلت أفكر في مسلسل (سواء كان ذلك من Microsoft أو من مكون 3rd الطرف) التي يمكن أن تستمر السلاسل حتى يحتوي على أحرف بديلة غير مزاوجة ؛ أنا غوغل هذا كل الآن وبعد ذلك: التسلسل إزاحة شخصية بديلة . NET . هذا لا يجعلني أخسر أي نوم ، لكنه مزعج نوعًا ما بين الحين والآخر ، هناك شخص ما يعلق على جوابي بأنه معيوب ، ومع ذلك فإن إجاباتهم تشوبها عيوب متساوية عندما يتعلق الأمر بشخصيات بديلة غير مترابطة.

الرنة ، مايكروسوفت يجب أن تستخدم فقط System.Buffer.BlockCopy في BinaryFormatter لها

谢谢!


يعتمد ذلك على ترميز السلسلة ( ASCII ، UTF-8 ، ...).

فمثلا:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

عينة صغيرة لماذا ترميز الأمور:

string pi = "\u03a0";
byte[] ascii = System.Text.Encoding.ASCII.GetBytes (pi);
byte[] utf8 = System.Text.Encoding.UTF8.GetBytes (pi);

Console.WriteLine (ascii.Length); //Will print 1
Console.WriteLine (utf8.Length); //Will print 2
Console.WriteLine (System.Text.Encoding.ASCII.GetString (ascii)); //Will print '?'

ASCII ببساطة ليست مجهزة للتعامل مع الشخصيات الخاصة.

داخليًا ، يستخدم .NET framework UTF-16 لتمثيل السلاسل ، لذلك إذا أردت الحصول على البايتات التي تستخدمها .NET ، استخدم System.Text.Encoding.Unicode.GetBytes (...) .

راجع تشفير الأحرف في .NET Framework (MSDN) لمزيد من المعلومات.


Fastest way

public static byte[] GetBytes(string text)
{
    return System.Text.ASCIIEncoding.UTF8.GetBytes(text);
}

EDIT as Makotosan commented this is now the best way:

Encoding.UTF8.GetBytes(text)

From byte[] to string :

        return BitConverter.ToString(bytes);

Here is my unsafe implementation of String to Byte[] conversion:

public static unsafe Byte[] GetBytes(String s)
{
    Int32 length = s.Length * sizeof(Char);
    Byte[] bytes = new Byte[length];

    fixed (Char* pInput = s)
    fixed (Byte* pBytes = bytes)
    {
        Byte* source = (Byte*)pInput;
        Byte* destination = pBytes;

        if (length >= 16)
        {
            do
            {
                *((Int64*)destination) = *((Int64*)source);
                *((Int64*)(destination + 8)) = *((Int64*)(source + 8));

                source += 16;
                destination += 16;
            }
            while ((length -= 16) >= 16);
        }

        if (length > 0)
        {
            if ((length & 8) != 0)
            {
                *((Int64*)destination) = *((Int64*)source);

                source += 8;
                destination += 8;
            }

            if ((length & 4) != 0)
            {
                *((Int32*)destination) = *((Int32*)source);

                source += 4;
                destination += 4;
            }

            if ((length & 2) != 0)
            {
                *((Int16*)destination) = *((Int16*)source);

                source += 2;
                destination += 2;
            }

            if ((length & 1) != 0)
            {
                ++source;
                ++destination;

                destination[0] = source[0];
            }
        }
    }

    return bytes;
}

It's way faster than the accepted anwser's one, even if not as elegant as it is. Here are my Stopwatch benchmarks over 10000000 iterations:

[Second String: Length 20]
Buffer.BlockCopy: 746ms
Unsafe: 557ms

[Second String: Length 50]
Buffer.BlockCopy: 861ms
Unsafe: 753ms

[Third String: Length 100]
Buffer.BlockCopy: 1250ms
Unsafe: 1063ms

In order to use it, you have to tick "Allow Unsafe Code" in your project build properties. As per .NET Framework 3.5, this method can also be used as String extension:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}

If you really want a copy of the underlying bytes of a string, you can use a function like the one that follows. However, you shouldn't please read on to find out why.

[DllImport(
        "msvcrt.dll",
        EntryPoint = "memcpy",
        CallingConvention = CallingConvention.Cdecl,
        SetLastError = false)]
private static extern unsafe void* UnsafeMemoryCopy(
    void* destination,
    void* source,
    uint count);

public static byte[] GetUnderlyingBytes(string source)
{
    var length = source.Length * sizeof(char);
    var result = new byte[length];
    unsafe
    {
        fixed (char* firstSourceChar = source)
        fixed (byte* firstDestination = result)
        {
            var firstSource = (byte*)firstSourceChar;
            UnsafeMemoryCopy(
                firstDestination,
                firstSource,
                (uint)length);
        }
    }

    return result;
}

This function will get you a copy of the bytes underlying your string, pretty quickly. You'll get those bytes in whatever way they are encoding on your system. This encoding is almost certainly UTF-16LE but that is an implementation detail you shouldn't have to care about.

It would be safer, simpler and more reliable to just call,

System.Text.Encoding.Unicode.GetBytes()

In all likelihood this will give the same result, is easier to type, and the bytes will always round-trip with a call to

System.Text.Encoding.Unicode.GetString()

Simply use this:

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

The key issue is that a glyph in a string takes 32 bits (16 bits for a character code) but a byte only has 8 bits to spare. A one-to-one mapping doesn't exist unless you restrict yourself to strings that only contain ASCII characters. System.Text.Encoding has lots of ways to map a string to byte[], you need to pick one that avoids loss of information and that is easy to use by your client when she needs to map the byte[] back to a string.

Utf8 is a popular encoding, it is compact and not lossy.


The string can be converted to byte array in few different ways, due to the following fact: .NET supports Unicode, and Unicode standardizes several difference encodings called UTFs. They have different lengths of byte representation but are equivalent in that sense that when a string is encoded, it can be coded back to the string, but if the string is encoded with one UTF and decoded in the assumption of different UTF if can be screwed up.

Also, .NET supports non-Unicode encodings, but they are not valid in general case (will be valid only if a limited sub-set of Unicode code point is used in an actual string, such as ASCII). Internally, .NET supports UTF-16, but for stream representation, UTF-8 is usually used. It is also a standard-de-facto for Internet.

Not surprisingly, serialization of string into an array of byte and deserialization is supported by the class System.Text.Encoding , which is an abstract class; its derived classes support concrete encodings: ASCIIEncoding and four UTFs ( System.Text.UnicodeEncoding supports UTF-16)

Ref this link.

For serialization to an array of bytes using System.Text.Encoding.GetBytes . For the inverse operation use System.Text.Encoding.GetChars . This function returns an array of characters, so to get a string, use a string constructor System.String(char[]) .
Ref this page.

مثال:

string myString = //... some string

System.Text.Encoding encoding = System.Text.Encoding.UTF8; //or some other, but prefer some UTF is Unicode is used
byte[] bytes = encoding.GetBytes(myString);

//next lines are written in response to a follow-up questions:

myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);
myString = new string(encoding.GetChars(bytes));
byte[] bytes = encoding.GetBytes(myString);

//how many times shall I repeat it to show there is a round-trip? :-)

With the advent of Span<T> released with C# 7.2, the canonical technique to capture the underlying memory representation of a string into a managed byte array is:

byte[] bytes = "rubbish_\u9999_string".AsSpan().AsBytes().ToArray();

Converting it back should be a non-starter because that means you are in fact interpreting the data somehow, but for the sake of completeness:

string s;
unsafe
{
    fixed (char* f = &bytes.AsSpan().NonPortableCast<byte, char>().DangerousGetPinnableReference())
    {
        s = new string(f);
    }
}

The names NonPortableCast and DangerousGetPinnableReference should further the argument that you probably shouldn't be doing this.

Note that working with Span<T> requires installing the System.Memory NuGet package .

Regardless, the actual original question and follow-up comments imply that the underlying memory is not being "interpreted" (which I assume means is not modified or read beyond the need to write it as-is), indicating that some implementation of the Stream class should be used instead of reasoning about the data as strings at all.


You can use following code to convert a string to a byte array in .NET

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);

BinaryFormatter bf = new BinaryFormatter();
byte[] bytes;
MemoryStream ms = new MemoryStream();

string orig = "喂 Hello 谢谢 Thank You";
bf.Serialize(ms, orig);
ms.Seek(0, 0);
bytes = ms.ToArray();

MessageBox.Show("Original bytes Length: " + bytes.Length.ToString());

MessageBox.Show("Original string Length: " + orig.Length.ToString());

for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo encrypt
for (int i = 0; i < bytes.Length; ++i) bytes[i] ^= 168; // pseudo decrypt

BinaryFormatter bfx = new BinaryFormatter();
MemoryStream msx = new MemoryStream();            
msx.Write(bytes, 0, bytes.Length);
msx.Seek(0, 0);
string sx = (string)bfx.Deserialize(msx);

MessageBox.Show("Still intact :" + sx);

MessageBox.Show("Deserialize string Length(still intact): " 
    + sx.Length.ToString());

BinaryFormatter bfy = new BinaryFormatter();
MemoryStream msy = new MemoryStream();
bfy.Serialize(msy, sx);
msy.Seek(0, 0);
byte[] bytesy = msy.ToArray();

MessageBox.Show("Deserialize bytes Length(still intact): " 
   + bytesy.Length.ToString());

byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}




string