c# obtener los - ¿Cómo obtengo una representación de bytes consistente de cadenas en C # sin especificar manualmente una codificación?





15 Answers

Depende de la codificación de su 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 de 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 marco .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 (...) .

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

ultimos caracteres string

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

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

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




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



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

Necesidad Común

Cada cadena tiene un conjunto de caracteres y codificación. Cuando convierte un objeto System.String en una matriz de System.Byte , todavía tiene un conjunto de caracteres y codificación. Para la mayoría de los usos, sabría qué conjunto de caracteres y codificación necesita y .NET facilita la "copia con conversión". Simplemente elija la clase de Encoding apropiada.

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

Es posible que la conversión deba manejar casos en los que el conjunto de caracteres o la codificación de destino no sean compatibles con un carácter que esté en el origen. Tienes algunas opciones: excepción, sustitución o saltar. 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érdida!

Nota: para System.String el conjunto de caracteres de origen 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 conjunto de caracteres. Encoding.Unicode debe llamarse Encoding.UTF16 .

Eso es todo para la mayoría de los usos. Si eso es lo que necesitas, deja de leer aquí. Vea el divertido joelonsoftware.com/articles/Unicode.html si no entiende qué es una codificación.

Necesidad especifica

Ahora, el autor 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.

De la especificación de C # :

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

Entonces, 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 las 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 de bytes mediante BlockCopy . Sin embargo, esto copia la cadena dos veces! Y, también utiliza explícitamente el código específico de la codificación: el tipo de datos System.Char .

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

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

Para hacerlo, el compilador escribe código de salto sobre las otras partes del objeto de cadena con RuntimeHelpers.OffsetToStringData . Entonces, para obtener los bytes sin procesar, simplemente cree un puntero a la cadena y copie la cantidad 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 endianness de la máquina. Pero a la pregunta del autor no le preocupa eso.




La primera parte de su pregunta (cómo obtener los bytes) ya fue respondida por otros: busque en el System.Text.Encoding nombres System.Text.Encoding .

Abordaré su pregunta de seguimiento: ¿por qué necesita elegir una codificación? ¿Por qué no puedes obtener eso de la propia clase de cadena?

La respuesta está en dos partes.

En primer lugar, los bytes utilizados internamente por la clase de cadena no importan , y siempre que asuma que lo hacen, es probable que esté introduciendo un error.

Si su programa está completamente dentro del mundo .Net, entonces no necesita preocuparse por obtener matrices de bytes para cadenas, incluso si está enviando datos a través de una red. En su lugar, use .Net Serialization para preocuparse por la transmisión de datos. Ya no se preocupa por los bytes reales: el formateador de serialización lo hace por usted.

Por otro lado, ¿qué sucede si está enviando estos bytes a algún lugar que no puede garantizar que extraerá datos de un flujo serializado .Net? En este caso, definitivamente debe preocuparse por la codificación, porque obviamente a este sistema externo le importa. De nuevo, los bytes internos utilizados por la cadena no importan: debe elegir una codificación para que pueda ser explícito acerca de esta codificación en el extremo receptor, incluso si es la misma codificación utilizada internamente por .Net.

Entiendo que en este caso, es posible que prefiera usar los bytes reales almacenados por la variable de cadena en la memoria siempre que sea posible, con la idea de que podría ahorrar algo de trabajo al crear su flujo de bytes. Sin embargo, te lo pongo, no es importante en comparación con asegurarte de que tu salida se comprenda en el otro extremo y garantizar que debes ser explícito con tu codificación. Además, si realmente desea hacer coincidir sus bytes internos, ya puede elegir la codificación Unicode y obtener ese ahorro de rendimiento.

Lo que me lleva a la segunda parte ... elegir la codificación Unicode es decirle a .Net que use los bytes subyacentes. Es necesario que elija esta codificación, porque cuando aparece Unicode-Plus con nuevos colmillos, el tiempo de ejecución .Net debe ser libre para usar este modelo de codificación mejor y más nuevo sin interrumpir su programa. Pero, por el momento (y el futuro previsible), con solo elegir la codificación Unicode, obtendrá lo que desea.

También es importante entender que la cadena debe volver a escribirse en el cable, y eso implica al menos una traducción del patrón de bits, incluso cuando se utiliza una codificación coincidente . La computadora debe tener en cuenta cosas como Big vs Little Endian, orden de bytes de la red, paquetización, información de sesión, etc.




Bueno, he leído todas las respuestas y trataron sobre el uso de codificación o una sobre la serialización que elimina sustitutos no pareados.

Es malo cuando la cadena, por ejemplo, proviene de SQL Server, donde se creó a partir de una matriz de bytes, por ejemplo, un hash de contraseña. Si eliminamos algo de él, almacenará un hash no válido y si queremos almacenarlo en XML, queremos dejarlo intacto (porque el escritor XML elimina una excepción en cualquier sustituto no pareado que encuentre).

Así que uso la codificación Base64 de matrices de bytes en tales casos, pero bueno, en Internet solo hay una solución para esto en C #, y tiene errores y es solo de una manera, así que arreglé el error y lo escribí. procedimiento. Aquí tienes, futuros googlers:

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 # para convertir a stringa una bytematriz:

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



Puede usar el siguiente código para la conversión entre la cadena y la matriz de bytes.

string s = "Hello World";

// String to Byte[]

byte[] byte1 = System.Text.Encoding.Default.GetBytes(s);

// OR

byte[] byte2 = System.Text.ASCIIEncoding.Default.GetBytes(s);

// Byte[] to string

string str = System.Text.Encoding.UTF8.GetString(byte1);



El problema clave es que un glifo en una cadena toma 32 bits (16 bits para un código de caracteres) pero un byte solo tiene 8 bits de sobra. No existe una asignación uno a uno a menos que se limite a cadenas que solo contengan caracteres ASCII. System.Text.Encoding tiene muchas formas de asignar una cadena a byte [], debe elegir una que evite la pérdida de información y que sea fácil de usar por su cliente cuando necesite asignar el byte [] a una cadena .

Utf8 es una codificación popular, es compacta y no tiene pérdidas.




Utilizar:

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

El resultado es:

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



El enfoque más cercano a la pregunta del OP es Tom Blodget, que en realidad entra en el objeto y extrae los bytes. Digo más cerca porque depende de la implementación del objeto String.

"Can't I simply get what bytes the string has been stored in?"

Claro, pero ahí es donde surge el error fundamental en la pregunta. La cadena es un objeto que podría tener una estructura de datos interesante. Ya sabemos que lo hace, porque permite que se almacenen sustitutos no pareados. Podría almacenar la longitud. Puede mantener un puntero a cada uno de los sustitutos "emparejados" que permite un conteo rápido. Etc. Todos estos bytes adicionales no son parte de los datos de caracteres.

Lo que quieres son los bytes de cada carácter en una matriz. Y ahí es donde entra la "codificación". De forma predeterminada, obtendrá UTF-16LE. Si no le importan los bytes, excepto el viaje de ida y vuelta, puede elegir cualquier codificación, incluida la "predeterminada", y volver a convertirla más tarde (asumiendo los mismos parámetros, como cuál fue la codificación predeterminada, los puntos de código, las correcciones de errores). , cosas permitidas tales como sustitutos no pareados, etc.

Pero ¿por qué dejar la 'codificación' a la magia? ¿Por qué no especificar la codificación para que sepa qué bytes obtendrá?

"Why is there a dependency on character encodings?"

La codificación (en este contexto) simplemente significa los bytes que representan su cadena. No los bytes del objeto cadena. Quería los bytes en los que se había almacenado la cadena; aquí es donde se hizo la pregunta ingenuamente. Quería los bytes de la cadena en una matriz contigua que representa la cadena y no todos los demás datos binarios que un objeto de cadena puede contener.

Lo que significa que una cadena se almacena es irrelevante. Desea una cadena "Codificada" en bytes en una matriz de bytes.

Me gusta la respuesta de Tom Bloget porque te llevó hacia la dirección de 'bytes de la cadena de objetos'. Sin embargo, depende de la implementación, y debido a que está mirando hacia adentro, puede ser difícil reconstituir una copia de la cadena.

La respuesta de Mehrdad es incorrecta porque es engañosa en el nivel conceptual. Todavía tienes una lista de bytes, codificados. Su solución particular permite preservar sustitutos no pareados, esto depende de la implementación. Su solución particular no produciría los bytes de la cadena con precisión si se GetBytesdevolviera la cadena en UTF-8 de forma predeterminada.

He cambiado de opinión sobre esto (la solución de Mehrdad): esto no está obteniendo los bytes de la cadena; más bien está obteniendo los bytes de la matriz de caracteres que se creó a partir de la cadena. Independientemente de la codificación, el tipo de datos char en c # es un tamaño fijo. Esto permite que se produzca una matriz de bytes de longitud constante, y permite que la matriz de caracteres se reproduzca en función del tamaño de la matriz de bytes. Entonces, si la codificación fuera UTF-8, pero cada caracter fuera de 6 bytes para acomodar el mayor valor de utf8, aún funcionaría. Así que, de hecho, la codificación del personaje no importa.

Pero se usó una conversión: cada carácter se colocó en un cuadro de tamaño fijo (tipo de carácter de c #). Sin embargo, no importa qué es esa representación, que técnicamente es la respuesta al OP. Entonces, si vas a convertir de todos modos ... ¿Por qué no 'codificar'?




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

Una string en .NET representa el texto como una secuencia de unidades de código UTF-16, por lo que los bytes ya están codificados en la memoria en UTF-16.

La respuesta de Mehrdad

Puedes usar la respuesta de Mehrdad , pero en realidad usa una codificación porque los caracteres son UTF-16. Llama a ToCharArray, que al ver la fuente crea char[]y copia la memoria directamente. Luego copia los datos a una matriz de bytes que también se asigna. Así que debajo del capó está copiando los bytes subyacentes dos veces y asignando una matriz de caracteres que no se usa después de la llamada.

La respuesta de Tom Blodget

La respuesta de Tom Blodget es un 20-30% más rápida que la de Mehrdad, ya que omite el paso intermedio de asignar una matriz de caracteres y copiar los bytes en ella, pero requiere que compiles con la /unsafeopción. Si no desea utilizar la codificación, creo que este es el camino a seguir. Si coloca su inicio de sesión de cifrado dentro del fixedbloque, ni siquiera necesita asignar una matriz de bytes separada y copiar los bytes en ella.

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

Porque esa es la forma correcta de hacerlo. stringEs una abstracción.

El uso de una codificación podría causarle problemas si tiene 'cadenas' con caracteres no válidos, pero eso no debería suceder. Si está obteniendo datos en su cadena con caracteres no válidos, lo está haciendo mal. Probablemente debería estar usando una matriz de bytes o una codificación Base64 para comenzar.

Si lo usas System.Text.Encoding.Unicode, tu código será más resistente. No tiene que preocuparse por la endianness del sistema en el que se ejecutará su código. No debe preocuparse si la próxima versión de CLR usará una codificación de caracteres interna diferente.

Creo que la pregunta no es por qué quieres preocuparte por la codificación, sino por qué quieres ignorarla y usar otra cosa. La codificación está destinada a representar la abstracción de una cadena en una secuencia de bytes. System.Text.Encoding.Unicodele dará una pequeña codificación de bytes de bytes endian y funcionará de la misma manera en todos los sistemas, ahora y en el futuro.




Dos caminos:

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

Y,

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

Tiendo a usar el de abajo más a menudo que el de arriba, no los he evaluado para la velocidad.




código simple con LINQ

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

EDITAR: como se comenta a continuación, no es una buena manera.

pero todavía puedes usarlo para entender LINQ con una codificación más apropiada:

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



Simplemente usa esto:

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



Depende de lo que quieras para los bytes.

Esto se debe a que, como Tyler lo said tan acertadamente , "las cadenas no son datos puros. También tienen information ". En este caso, la información es una codificación que se asumió cuando se creó la cadena.

Suponiendo que tiene datos binarios (en lugar de texto) almacenados en una cadena

Esto se basa en el comentario de OP sobre su propia pregunta, y es la pregunta correcta si entiendo las sugerencias de OP sobre el caso de uso.

¡Almacenar datos binarios en cadenas es probablemente el enfoque incorrecto debido a la supuesta codificación mencionada anteriormente! Cualquier programa o biblioteca que haya almacenado esos datos binarios en un string(en lugar de una byte[]matriz que hubiera sido más apropiado) ya ha perdido la batalla antes de que haya comenzado. Si le envían los bytes en una solicitud / respuesta REST o cualquier cosa que deba transmitir cadenas, Base64 sería el enfoque correcto.

Si tienes una cadena de texto con una codificación desconocida

Todos los demás respondieron esta pregunta incorrecta incorrectamente.

Si la cadena se ve bien como está, simplemente elija una codificación (preferiblemente una que comience con UTF), use la System.Text.Encoding.???.GetBytes()función correspondiente y dígale a quien le da los bytes a la codificación que eligió.




Related