Pregunta ¿Cómo obtengo una representación de bytes consistente en cadenas en C # sin especificar manualmente una codificación?


¿Cómo convierto un string a un byte[] en .NET (C #) sin especificar manualmente una codificación específica?

Voy a cifrar la cadena. Puedo encriptarlo sin convertirlo, pero aún me gustaría saber por qué la codificación viene a jugar aquí.

Además, ¿por qué debería tenerse en cuenta la codificación? ¿No puedo simplemente obtener los bytes en los que se ha almacenado la cadena? ¿Por qué hay una dependencia en las codificaciones de caracteres?


1909
2018-01-23 13:39


origen


Respuestas:


Al contrario de las respuestas aquí, NO necesitas preocuparte por la codificación Si los bytes no necesitan ser interpretados!

Como mencionaste, tu objetivo es, simplemente, "obtener en qué bytes se ha almacenado la cadena".
(Y, por supuesto, para poder reconstruir la cadena a partir de los bytes).

Para esos objetivos, honestamente hago no Comprenda por qué la gente le sigue diciendo que necesita las codificaciones. Ciertamente NO necesitas preocuparte por las codificaciones para esto.

Solo haz esto en su lugar:

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

Siempre que su programa (u otros programas) no intente interpretar los bytes de alguna manera, que obviamente no mencionaste que intentas hacer, luego está nada mal con este enfoque! Preocuparse por las codificaciones solo hace tu vida más complicada sin una razón real.

Beneficio adicional a este enfoque:

No importa si la cadena contiene caracteres no válidos, porque de todos modos puede obtener los datos y reconstruir la cadena original.

Se codificará y decodificará de la misma manera, porque eres solo mirando los bytes.

Sin embargo, si usaste una codificación específica, te hubiera dado problemas con la codificación / descodificación de caracteres no válidos.


1721
2018-04-30 07:44



Depende de la codificación de tu cadena (ASCII, UTF-8, ...).

Por ejemplo:

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

Una pequeña muestra por qué la codificación importa:

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 simplemente no está equipado para tratar con caracteres especiales.

Internamente, el framework .NET usa UTF-16 para representar cadenas, por lo que si simplemente desea obtener los bytes exactos que usa .NET, use System.Text.Encoding.Unicode.GetBytes (...).

Ver Codificación de caracteres en .NET Framework (MSDN) para más información.


1052
2018-01-23 13:43



La respuesta aceptada es muy, muy complicada. Use las clases .NET incluidas para esto:

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

No reinventes la rueda si no tienes que ...


245
2018-04-30 07:26



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

105
2018-01-23 16:36



Debe tener en cuenta la codificación, ya que 1 carácter podría estar representado por 1 o más los bytes (hasta aproximadamente 6) y las diferentes codificaciones tratarán estos bytes de forma diferente.

Joel tiene una publicación sobre esto:

El mínimo absoluto de cada desarrollador de software Absolutamente, definitivamente debe saber sobre Unicode y juegos de caracteres (¡Sin excusas!)


79
2018-01-23 14:03



Esta es una pregunta popular. Es importante entender lo que pregunta el autor de la pregunta, y que es diferente de lo que probablemente sea la necesidad más común. Para desalentar el mal uso del código donde no es necesario, primero he respondido el primero.

Necesidad común

Cada cadena tiene un conjunto de caracteres y codificación. Cuando conviertes un System.String objetar a una serie de System.Byte todavía tienes un conjunto de caracteres y codificación. Para la mayoría de los usos, sabrá qué conjunto de caracteres y codificación necesita y .NET simplifica la "copia con conversión". Simplemente elija el apropiado Encoding clase.

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

La conversión puede necesitar manejar casos donde el conjunto de caracteres objetivo o la codificación no admite un carácter que está en la fuente. Usted tiene algunas opciones: excepción, sustitución u omisión. La política predeterminada es sustituir un '?'.

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

Claramente, las conversiones no son necesariamente sin pérdidas.

Nota: para System.String el conjunto de caracteres fuente es Unicode.

Lo único confuso es que .NET usa el nombre de un conjunto de caracteres para el nombre de una codificación particular de ese juego de caracteres. Encoding.Unicode debería ser llamado Encoding.UTF16.

Eso es todo para la mayoría de los usos. Si eso es lo que necesitas, deja de leer aquí. Mira la diversión Artículo de Joel Spolsky si no entiendes lo que es una codificación

Necesidad específica

Ahora, el autor de la pregunta pregunta: "Cada cadena se almacena como una matriz de bytes, ¿verdad? ¿Por qué no puedo simplemente tener esos bytes?"

Él no quiere ninguna conversión.

Desde el C # spec:

El procesamiento de caracteres y cadenas en C # usa codificación Unicode. El char   tipo representa una unidad de código UTF-16, y el tipo de cadena representa un   secuencia de unidades de código UTF-16.

Por lo tanto, sabemos que si solicitamos la conversión nula (es decir, de UTF-16 a UTF-16), obtendremos el resultado deseado:

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

Pero para evitar la mención de codificaciones, debemos hacerlo de otra manera. Si un tipo de datos intermedio es aceptable, hay un atajo conceptual para esto:

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

Eso no nos da el tipo de datos deseado, pero La respuesta de Mehrdad muestra cómo convertir esta matriz Char en una matriz Byte usando BlockCopy. Sin embargo, ¡esto copia la cadena dos veces! Y, de manera muy explícita, utiliza un código específico de codificación: el tipo de datos System.Char.

La única manera de llegar a los bytes reales en los que se almacena la cadena es usar un puntero. los fixed La declaración permite tomar la dirección de los valores. De la especificación de C #:

[Para] una expresión de tipo cadena, ... el inicializador calcula el   dirección del primer caracter en la cadena.

Para hacerlo, el compilador escribe código omite sobre las otras partes del objeto de cadena con RuntimeHelpers.OffsetToStringData. Entonces, para obtener los bytes sin formato, solo cree un puntero a la cadena y copie el número de bytes necesarios.

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

Como señaló @CodesInChaos, el resultado depende de la endianidad de la máquina. Pero al autor de la pregunta no le preocupa eso.


76
2017-12-02 04:43



Solo para demostrar el sonido de Mehrdrad responder funciona, su enfoque puede incluso persistir el caracteres sustitutos sin parear(de los cuales muchos se han opuesto a mi respuesta, pero de los cuales todos son igualmente culpables, p. System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; esos métodos de codificación no pueden persistir los personajes de alto rango d800por ejemplo, y aquellos simplemente reemplazan a los personajes sustitutos altos con valor 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);
    }        
}

Salida:

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

Intenta eso con System.Text.Encoding.UTF8.GetBytes o System.Text.Encoding.Unicode.GetBytes, simplemente reemplazarán a los personajes sustitutos altos con valor fffd

Cada vez que hay un movimiento en esta pregunta, todavía estoy pensando en un serializador (ya sea de Microsoft o de un componente de terceros) que puede persistir cadenas, incluso si contiene caracteres sustitutos no pareados. Yo google esto de vez en cuando: carácter sustituto sin pareja de serialización .NET. Esto no me hace perder sueño, pero es algo molesto cuando de vez en cuando hay alguien que comenta mi respuesta que es defectuoso, pero sus respuestas son igualmente erróneas cuando se trata de personajes sustitutos no pareados.

Maldición, Microsoft debería haber usado System.Buffer.BlockCopy en su BinaryFormatter ツ

谢谢!


35
2017-07-25 22:52