.net string - Comment obtenir une représentation cohérente des octets en C#sans spécifier manuellement un encodage?




prefix substring (25)

Comment puis-je convertir une string en un byte[] dans .NET (C #) sans spécifier manuellement un codage spécifique?

Je vais crypter la chaîne. Je peux le crypter sans conversion, mais j'aimerais quand même savoir pourquoi l'encodage vient jouer ici.

De même, pourquoi l'encodage devrait-il être pris en compte? Je ne peux pas simplement obtenir les octets dans lesquels la chaîne a été stockée? Pourquoi y a-t-il une dépendance aux encodages de caractères?


Answers

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

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

Juste pour démontrer que la share sonore de Mehrdrad fonctionne, son approche peut même persister les [BinaryFormatter (dont beaucoup avaient résisté à ma réponse, mais dont tout le monde est également coupable, par exemple System.Text.Encoding.UTF8.GetBytes , System.Text.Encoding.Unicode.GetBytes ; ces méthodes de codage ne peuvent pas conserver les caractères de substitution élevés d800 par exemple, et ceux-ci remplacent simplement les caractères de substitution élevés par la valeur 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);
    }        
}

Sortie:

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

Essayez cela avec System.Text.Encoding.UTF8.GetBytes ou System.Text.Encoding.Unicode.GetBytes , ils remplaceront simplement les caractères de substitution élevés avec la valeur fffd

Chaque fois qu'il y a un mouvement dans cette question, je pense toujours à un sérialiseur (que ce soit de Microsoft ou d'un composant tiers) qui peut persister des chaînes même s'il contient des caractères de substitution non appariés; Je google ceci de temps en temps: serialization caractère de substitution non apparié .NET . Cela ne me fait pas perdre le sommeil, mais c'est un peu ennuyeux quand de temps en temps quelqu'un commente ma réponse qui est imparfaite, mais leurs réponses sont tout aussi imparfaites quand il s'agit de personnages substituts non appariés.

Darn, Microsoft aurait dû utiliser System.Buffer.BlockCopy dans son BinaryFormatter

谢谢!


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

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.

Exemple:

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? :-)

I'm not sure, but I think the string stores its info as an array of Chars, which is inefficient with bytes. Specifically, the definition of a Char is "Represents a Unicode character".

take this example sample:

String str = "asdf éß";
String str2 = "asdf gh";
EncodingInfo[] info =  Encoding.GetEncodings();
foreach (EncodingInfo enc in info)
{
    System.Console.WriteLine(enc.Name + " - " 
      + enc.GetEncoding().GetByteCount(str)
      + enc.GetEncoding().GetByteCount(str2));
}

Take note that the Unicode answer is 14 bytes in both instances, whereas the UTF-8 answer is only 9 bytes for the first, and only 7 for the second.

So if you just want the bytes used by the string, simply use Encoding.Unicode , but it will be inefficient with storage space.


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

Veuillez également expliquer pourquoi l'encodage doit être pris en compte. Je ne peux pas simplement obtenir les octets dans lesquels la chaîne a été stockée? Pourquoi cette dépendance sur l'encodage? !!!

Parce qu'il n'y a rien de tel que "les octets de la chaîne".

Une chaîne (ou plus généralement un texte) est composée de caractères: lettres, chiffres et autres symboles. C'est tout. Les ordinateurs, cependant, ne savent rien des caractères; ils peuvent seulement gérer des octets. Par conséquent, si vous souhaitez stocker ou transmettre du texte à l'aide d'un ordinateur, vous devez transformer les caractères en octets. Comment tu fais ça? Voici où les encodages viennent sur la scène.

Un encodage n'est rien d'autre qu'une convention pour traduire des caractères logiques en octets physiques. L'encodage le plus simple et le plus connu est ASCII, et c'est tout ce dont vous avez besoin si vous écrivez en anglais. Pour les autres langues, vous aurez besoin d'encodages plus complets, étant l'un des saveurs d'Unicode le choix le plus sûr de nos jours.

Bref, essayer d'obtenir les octets d'une chaîne sans utiliser d'encodage est aussi impossible que d'écrire un texte sans utiliser de langage.

En passant, je vous recommande fortement (et tout le monde, d'ailleurs) de lire ce petit morceau de sagesse: joelonsoftware.com/articles/Unicode.html


Simply use this:

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

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)

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

Cela dépend de l'encodage de votre chaîne ( ASCII , UTF-8 , ...).

Par exemple:

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

Un petit échantillon pourquoi l'encodage est important:

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 n'est tout simplement pas équipé pour traiter les caractères spéciaux.

En interne, le framework .NET utilise UTF-16 pour représenter les chaînes, donc si vous voulez simplement obtenir les octets exacts utilisés par .NET, utilisez System.Text.Encoding.Unicode.GetBytes (...) .

Voir Encodage des caractères dans .NET Framework (MSDN) pour plus d'informations.


From byte[] to string :

        return BitConverter.ToString(bytes);

Two ways:

public static byte[] StrToByteArray(this string s)
{
    List<byte> value = new List<byte>();
    foreach (char c in s.ToCharArray())
        value.Add(c.ToByte());
    return value.ToArray();
}

Et,

public static byte[] StrToByteArray(this string s)
{
    s = s.Replace(" ", string.Empty);
    byte[] buffer = new byte[s.Length / 2];
    for (int i = 0; i < s.Length; i += 2)
        buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
    return buffer;
}

I tend to use the bottom one more often than the top, haven't benchmarked them for speed.


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.


Utilisation:

    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

Contrairement aux réponses ici, vous n'avez pas besoin de vous soucier de l'encodage si les octets n'ont pas besoin d'être interprétés!

Comme vous l'avez mentionné, votre but est, simplement, de "savoir de quels octets la chaîne a été stockée" .
(Et, bien sûr, pour pouvoir reconstruire la chaîne à partir des octets.)

Pour ces objectifs, honnêtement, je ne comprends pas pourquoi les gens continuent à vous dire que vous avez besoin des encodages. Vous n'avez certainement pas besoin de s'inquiéter des encodages pour cela.

Fais simplement ceci à la place:

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

Tant que votre programme (ou d'autres programmes) n'essaie pas d' interpréter les octets d'une façon ou d'une autre, ce que vous n'avez manifestement pas mentionné, alors il n'y a rien de mal à cette approche! S'inquiéter des codages rend simplement votre vie plus compliquée sans raison réelle.

Avantage supplémentaire à cette approche:

Peu importe si la chaîne contient des caractères invalides, car vous pouvez toujours récupérer les données et reconstruire la chaîne d'origine de toute façon!

Il sera encodé et décodé de la même façon, car vous regardez juste les octets .

Cependant, si vous utilisiez un encodage spécifique, cela vous aurait causé des problèmes avec l'encodage / décodage des caractères non valides.


La réponse acceptée est très, très compliquée. Utilisez les classes .NET incluses pour cela:

const string data = "A string with international characters: Norwegian: ÆØÅæøå, Chinese: 喂 谢谢";
var bytes = System.Text.Encoding.UTF8.GetBytes(data);
var decoded = System.Text.Encoding.UTF8.GetString(bytes);

Ne réinventez pas la roue si vous n'avez pas à ...


C'est une question populaire. Il est important de comprendre ce que l'auteur de la question demande, et qu'il est différent de ce qui est probablement le besoin le plus commun. Pour décourager l'utilisation abusive du code là où ce n'est pas nécessaire, j'ai répondu le plus tard en premier.

Besoin commun

Chaque chaîne a un jeu de caractères et un encodage. Lorsque vous convertissez un objet System.String en un tableau de System.Byte vous disposez toujours d'un jeu de caractères et d'un codage. Pour la plupart des utilisations, vous connaissez le jeu de caractères et l'encodage dont vous avez besoin et .NET simplifie la «copie avec conversion». Choisissez simplement la classe d' Encoding appropriée.

// using System.Text;
Encoding.UTF8.GetBytes(".NET String to byte array")

La conversion peut devoir gérer des cas où le jeu de caractères cible ou l'encodage ne prend pas en charge un caractère figurant dans la source. Vous avez quelques choix: l'exception, la substitution ou le saut. La stratégie par défaut consiste à remplacer "?"

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

Clairement, les conversions ne sont pas nécessairement sans perte!

Remarque: Pour System.String le jeu de caractères source est Unicode.

La seule chose qui prête à confusion est que .NET utilise le nom d'un jeu de caractères pour le nom d'un codage particulier de ce jeu de caractères. Encoding.Unicode devrait être appelé Encoding.UTF16 .

C'est tout pour la plupart des usages. Si c'est ce dont vous avez besoin, arrêtez de lire ici. Voir l'article amusant joelonsoftware.com/articles/Unicode.html si vous ne comprenez pas ce qu'est un encodage.

Besoin spécifique

Maintenant, l'auteur de la question demande: «Chaque chaîne est stockée sous la forme d'un tableau d'octets, n'est-ce pas? Pourquoi ne puis-je simplement avoir ces octets?

Il ne veut pas de conversion.

De la spécification C # :

Le traitement des caractères et des chaînes en C # utilise le codage Unicode. Le type char représente une unité de code UTF-16 et le type de chaîne représente une séquence d'unités de code UTF-16.

Donc, nous savons que si nous demandons la conversion nulle (c'est-à-dire de UTF-16 à UTF-16), nous obtiendrons le résultat souhaité:

Encoding.Unicode.GetBytes(".NET String to byte array")

Mais pour éviter la mention des encodages, il faut le faire autrement. Si un type de données intermédiaire est acceptable, il existe un raccourci conceptuel pour cela:

".NET String to byte array".ToCharArray()

Cela ne nous donne pas le type de données désiré, mais la réponse de Mehrdad montre comment convertir ce tableau Char en un tableau Byte en utilisant BlockCopy . Cependant, cela copie la chaîne deux fois! De plus, il utilise explicitement le code spécifique à l'encodage: le type de données System.Char .

La seule façon d'obtenir les octets réels dans lesquels la chaîne est stockée est d'utiliser un pointeur. La déclaration fixed permet de prendre l'adresse des valeurs. De la spécification C #:

[Pour] une expression de type string, ... l'initialiseur calcule l'adresse du premier caractère de la chaîne.

Pour ce faire, le compilateur écrit le passage de code sur les autres parties de l'objet chaîne avec RuntimeHelpers.OffsetToStringData . Donc, pour obtenir les octets bruts, créez simplement un pointeur sur la chaîne et copiez le nombre d'octets requis.

// using System.Runtime.InteropServices
unsafe byte[] GetRawBytes(String s)
{
    if (s == null) return null;
    var codeunitCount = s.Length;
    /* We know that String is a sequence of UTF-16 codeunits 
       and such codeunits are 2 bytes */
    var byteCount = codeunitCount * 2; 
    var bytes = new byte[byteCount];
    fixed(void* pRaw = s)
    {
        Marshal.Copy((IntPtr)pRaw, bytes, 0, byteCount);
    }
    return bytes;
}

Comme l'a souligné @CodesInChaos, le résultat dépend de l'endianness de la machine. Mais l'auteur de la question n'est pas concerné par cela.


simple code with LINQ

string s = "abc"
byte[] b = s.Select(e => (byte)e).ToArray();

EDIT : as commented below, it is not a good way.

but you can still use it to understand LINQ with a more appropriate coding :

string s = "abc"
byte[] b = s.Cast<byte>().ToArray();

Eh bien, j'ai lu toutes les réponses et ils étaient sur l'utilisation de l'encodage ou sur la sérialisation qui laisse tomber les substituts non appariés.

C'est mauvais quand la chaîne, par exemple, vient de SQL Server où elle a été construite à partir d'un tableau d'octets stockant, par exemple, un hachage de mot de passe. Si nous en supprimons quelque chose, cela va stocker un hachage invalide, et si nous voulons le stocker en XML, nous voulons le laisser intact (parce que l'écrivain XML dépose une exception sur n'importe quel substitut non apparié qu'il trouve).

Donc, j'utilise l'encodage Base64 des tableaux d'octets dans de tels cas, mais bon, sur Internet il n'y a qu'une seule solution à cela en C #, et il y a un bogue dedans et c'est seulement une façon, donc j'ai corrigé le bogue procédure. Ici vous êtes, futurs googleurs:

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

C # pour convertir une string en un tableau d' byte :

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

Vous devez prendre en compte l'encodage, car 1 caractère peut être représenté par 1 ou plusieurs octets (jusqu'à environ 6), et différents encodages traiteront ces octets différemment.

Joel a un commentaire à ce sujet:

joelonsoftware.com/articles/Unicode.html


bytes[] buffer = UnicodeEncoding.UTF8.GetBytes(string something); //for converting to UTF then get its bytes

bytes[] buffer = ASCIIEncoding.ASCII.GetBytes(string something); //for converting to ascii then get its bytes

La réponse simple est qu'ils sont exactement à l'opposé l'un de l'autre.

Prenons un exemple pour illustrer:

l'ordinateur utilise l'unité de base très basique pour stocker et traiter l'information, elle n'a aucun sens pour les yeux humains.

par exemple, '\ xe4 \ xb8 \ xad \ xe6 \ x96 \ x87' est la représentation de deux caractères chinois, mais l'ordinateur ne sait que (c'est-à-dire imprimer ou stocker) ce sont des caractères chinois quand on leur donne un dictionnaire Mot chinois, dans ce cas, c'est un dictionnaire "utf-8", et il ne serait pas correct d'afficher le mot chinois prévu si vous regardez dans un dictionnaire différent ou faux (en utilisant une méthode de décodage différente).

Dans le cas ci-dessus, le processus de recherche d'un mot chinois par un ordinateur est le décodage ().

Et le processus d'écriture de l'ordinateur chinois dans la mémoire de l'ordinateur est encoder ().

donc l'information de codage est les octets bruts, et les informations décodées sont les octets bruts et le nom du dictionnaire à référencer (mais pas le dictionnaire lui-même).





c# .net string