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

2190

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

Voy a encriptar la cadena. Puedo encriptarlo sin convertirlo, pero todavía me gustaría saber por qué la codificación se juega aquí.

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

Agnel Kurian
fuente
23
Cada cadena se almacena como una matriz de bytes, ¿verdad? ¿Por qué no puedo simplemente tener esos bytes?
Agnel Kurian
135
La codificación es lo que asigna los caracteres a los bytes. Por ejemplo, en ASCII, la letra 'A' se asigna al número 65. En una codificación diferente, podría no ser la misma. Sin embargo, el enfoque de alto nivel para las cadenas tomadas en el marco .NET hace que esto sea irrelevante (excepto en este caso).
Lucas Jones el
20
Para jugar al abogado del diablo: Si quisieras obtener los bytes de una cadena en memoria (como los usa .NET) y manipularlos de alguna manera (es decir, CRC32), y NUNCA quisiste decodificarlos nuevamente en la cadena original ... no es sencillo por qué te importarían las codificaciones o cómo eliges cuál usar.
Greg
79
Sorprendido, nadie ha dado este enlace todavía: joelonsoftware.com/articles/Unicode.html
Bevan
28
Un char no es un byte y un byte no es un char. Un carácter es tanto una clave en una tabla de fuentes como una tradición léxica. Una cadena es una secuencia de caracteres. (Las palabras, párrafos, oraciones y títulos también tienen sus propias tradiciones léxicas que justifican sus propias definiciones de tipo, pero estoy divagando). Al igual que los números enteros, los números de coma flotante y todo lo demás, los caracteres se codifican en bytes. Hubo un tiempo en que la codificación era simple uno a uno: ASCII. Sin embargo, para acomodar toda la simbología humana, las 256 permutaciones de un byte fueron insuficientes y se diseñaron codificaciones para utilizar selectivamente más bytes.
George

Respuestas:

1855

Contrariamente a las respuestas aquí, ¡NO tiene que preocuparse por la codificación si los bytes no necesitan ser interpretados!

Como mencionó, su 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, sinceramente, no entiendo por qué la gente sigue diciéndote que necesitas las codificaciones. Ciertamente NO necesita preocuparse 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;
}

// Do NOT use on arbitrary bytes; only use on GetBytes's output on the SAME system
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);
}

Mientras su programa (u otros programas) no intente interpretar los bytes de alguna manera, lo que obviamente no mencionó que tiene la intención de hacer, ¡entonces no hay nada de malo en este enfoque! Preocuparse por las codificaciones solo hace que su vida sea más complicada sin ninguna razón real.

Beneficio adicional de este enfoque:

¡No importa si la cadena contiene caracteres no válidos, ya que de todos modos puede obtener los datos y reconstruir la cadena original!

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

Sin embargo, si utilizó una codificación específica, le habría dado problemas para codificar / decodificar caracteres no válidos.

usuario541686
fuente
248
Lo feo de este es, eso GetStringy la GetBytesnecesidad de ejecutarlo en un sistema con la misma resistencia para trabajar. Por lo tanto, no puede usar esto para obtener bytes que desea convertir en una cadena en otro lugar. Así que tengo dificultades para encontrar situaciones en las que me gustaría usar esto.
CodesInChaos
72
@CodeInChaos: Como dije, el objetivo de todo esto es si quieres usarlo en el mismo tipo de sistema, con el mismo conjunto de funciones. Si no, entonces no deberías usarlo.
user541686
193
-1 Garantizo que alguien (que no entiende bytes vs caracteres) querrá convertir su cadena en una matriz de bytes, lo buscará en Google y leerá esta respuesta, y hará lo incorrecto, porque en casi todos casos, la codificación es relevante.
artbristol
401
@artbristol: si no pueden molestarse en leer la respuesta (o las otras respuestas ...), lo siento, entonces no hay mejor manera de comunicarme con ellos. Por lo general, opto por responder el OP en lugar de tratar de adivinar lo que otros podrían hacer con mi respuesta: el OP tiene el derecho de saber, y solo porque alguien pueda abusar de un cuchillo no significa que debamos esconder todos los cuchillos en el mundo para nosotros. Aunque si no estás de acuerdo, también está bien.
user541686
185
Esta respuesta es incorrecta en muchos niveles, pero sobre todo debido a su declinación "¡NO tiene que preocuparse por la codificación!". Los 2 métodos, GetBytes y GetString son superfluos en la medida en que son simplemente implementaciones de lo que ya hacen Encoding.Unicode.GetBytes () y Encoding.Unicode.GetString (). La declaración "siempre y cuando su programa (u otros programas) no intente interpretar los bytes" también es fundamentalmente defectuosa, ya que implícitamente significan que los bytes deben interpretarse como Unicode.
David
1108

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é es importante la codificación:

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.

bmotmans
fuente
14
Pero, ¿por qué se debe tener en cuenta la codificación? ¿Por qué no puedo simplemente obtener los bytes sin tener que ver qué codificación se está utilizando? Incluso si fuera necesario, ¿no debería el objeto String saber qué codificación se está utilizando y simplemente volcar lo que hay en la memoria?
Agnel Kurian
57
Las cadenas .NET siempre se codifican como Unicode. Entonces use System.Text.Encoding.Unicode.GetBytes (); para obtener el conjunto de bytes que .NET usaría para representar los caracteres. Sin embargo, ¿por qué quieres eso? Recomiendo UTF-8, especialmente cuando la mayoría de los personajes están en el conjunto del latín occidental.
AnthonyWJones el
8
Además: los bytes exactos utilizados internamente en la cadena no importan si el sistema que los recupera no maneja esa codificación o la maneja como la codificación incorrecta. Si todo está dentro de .Net, ¿por qué convertir a una matriz de bytes? De lo contrario, es mejor ser explícita con su codificación
Joel Coehoorn
11
@ Joel, tenga cuidado con System.Text.Encoding.Default ya que podría ser diferente en cada máquina que se ejecuta. Es por eso que se recomienda especificar siempre una codificación, como UTF-8.
Ash
25
No necesita las codificaciones a menos que usted (u otra persona) realmente tenga la intención de interpretar los datos, en lugar de tratarlos como un "bloque de bytes" genérico. Para cosas como la compresión, el cifrado, etc., preocuparse por la codificación no tiene sentido. Vea mi respuesta para una forma de hacer esto sin preocuparse por la codificación. (Podría haber dado un -1 por decir que necesita preocuparse por las codificaciones cuando no lo hace, pero no me siento particularmente mal hoy.: P)
user541686
285

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 reinvente la rueda si no tiene que ...

Erik A. Brandstadmoen
fuente
14
En caso de que la respuesta aceptada se modifique, para fines de registro, es la respuesta de Mehrdad en este momento y fecha actuales. Esperemos que el OP revisite esto y acepte una mejor solución.
Thomas Eding
77
bueno en principio, pero la codificación debe System.Text.Encoding.Unicodeser equivalente a la respuesta de Mehrdad.
Jodrell
55
La pregunta se ha editado miles de veces desde la respuesta original, por lo tanto, tal vez mi respuesta esté un poco desactualizada. Nunca tuve la intención de dar un equivalente exacto a la respuesta de Mehrdad, sino dar una forma sensata de hacerlo. Pero, puede que tengas razón. Sin embargo, la frase "obtener en qué bytes se ha almacenado la cadena" en la pregunta original es muy poco precisa. Almacenado, donde? ¿En memoria? En el disco? Si en la memoria, System.Text.Encoding.Unicode.GetBytesprobablemente sería más preciso.
Erik A. Brandstadmoen
77
@ AMissico, su sugerencia tiene errores, a menos que esté seguro de que su cadena es compatible con la codificación predeterminada de su sistema (cadena que contiene solo caracteres ASCII en el conjunto de caracteres heredado predeterminado de su sistema). Pero en ninguna parte el OP dice eso.
Frédéric
55
@ AMissico Sin embargo, puede hacer que el programa dé diferentes resultados en diferentes sistemas . Eso nunca es bueno. Incluso si es para hacer un hash o algo (supongo que eso es lo que OP significa con 'cifrar'), la misma cadena siempre debe dar el mismo hash.
Nyerguds
114
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());
Michael Buen
fuente
2
Podría usar la misma instancia de BinaryFormatter para todas esas operaciones
Joel Coehoorn el
3
Muy interesante. Aparentemente, eliminará cualquier personaje Unicode sustituto alto. Consulte la documentación en [BinaryFormatter ]
95

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

Joel tiene una publicación sobre esto:

El mínimo absoluto que todo desarrollador de software debe saber absolutamente, positivamente sobre los conjuntos de caracteres y Unicode (¡sin excusas!)

Zhaph - Ben Duguid
fuente
66
"1 carácter podría estar representado por 1 o más bytes" Estoy de acuerdo. Solo quiero esos bytes, independientemente de la codificación de la cadena. La única forma de almacenar una cadena en la memoria es en bytes. Incluso los caracteres se almacenan como 1 o más bytes. Simplemente quiero tener en mis manos esos bytes.
Agnel Kurian
16
No necesita las codificaciones a menos que usted (u otra persona) realmente tenga la intención de interpretar los datos, en lugar de tratarlos como un "bloque de bytes" genérico. Para cosas como la compresión, el cifrado, etc., preocuparse por la codificación no tiene sentido. Vea mi respuesta para una forma de hacer esto sin preocuparse por la codificación.
user541686
99
@Mehrdad - Totalmente, pero la pregunta original, como se indicó cuando respondí inicialmente, no indicaba qué OP iba ​​a suceder con esos bytes después de que los convirtieran, y para los futuros buscadores la información es pertinente: esta es cubierto por la respuesta de Joel bastante bien, y como usted dice dentro de su respuesta: siempre que permanezca dentro del mundo .NET y use sus métodos para convertir a / desde, está contento. Tan pronto como salgas de eso, la codificación será importante.
Zhaph - Ben Duguid
Un punto de código puede representarse con hasta 4 bytes. (Una unidad de código UTF-32, un par sustituto UTF-16 o 4 bytes de UTF-8.) Los valores para los que UTF-8 necesitaría más de 4 bytes están fuera del rango 0x0..0x10FFFF de Unicode. ;-)
DevSolar
89

Esta es una pregunta popular. Es importante comprender lo que hace 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, he respondido el primero primero.

Necesidad Común

Cada cadena tiene un conjunto de caracteres y codificación. Cuando convierte un System.Stringobjeto en una matriz System.Byte, todavía tiene un conjunto de caracteres y una codificación. Para la mayoría de los usos, sabría qué conjunto de caracteres y codificación necesita y .NET simplifica la tarea de "copiar con conversión". Simplemente elija la Encodingclase apropiada .

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

La conversión puede necesitar manejar casos en los que el conjunto de caracteres o la codificación de destino no admite un carácter que está en la fuente. 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.Stringel juego 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.Unicodedebe ser llamado 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 artículo de Joel Spolsky si no entiende lo que es una codificación.

Necesidad Específica

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

No quiere ninguna conversión.

De la especificación 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 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 también utiliza explícitamente el código específico de codificación: el tipo de datos System.Char.

La única forma de llegar a los bytes reales en los que se almacena la Cadena es usar un puntero. El fixedenunciado permite tomar la dirección de los valores. De la especificación 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 el salto de código 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 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 resistencia de la máquina. Pero el autor de la pregunta no está preocupado por eso.

Tom Blodget
fuente
3
@ Jan Eso es correcto, pero la longitud de la cadena ya da el número de unidades de código (no puntos de código).
Tom Blodget
1
¡Gracias por señalar eso! Desde MSDN: "La Lengthpropiedad [de String] devuelve el número de Charobjetos en esta instancia, no el número de caracteres Unicode". Por lo tanto, su código de ejemplo es correcto tal como está escrito.
Jan Hettich
1
@supercat "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". —_ Especificación C # 5._ Aunque, sí, no hay nada que evite una cadena Unicode no válida:new String(new []{'\uD800', '\u0030'})
Tom Blodget
1
@TomBlodget: Curiosamente, si uno toma instancias de Globalization.SortKey, extrae KeyDatay empaqueta los bytes resultantes de cada uno en un String[dos bytes por carácter, MSB primero ], invocar String.CompareOrdinallas cadenas resultantes será sustancialmente más rápido que invocar SortKey.Comparelas instancias de SortKey, o incluso recurriendo memcmpa esas instancias. Dado eso, me pregunto por qué KeyDatadevuelve un en Byte[]lugar de un String?
supercat
1
Por desgracia, la respuesta correcta, pero años demasiado tarde, nunca tendrá tantos votos como los aceptados. Debido a TL; la gente de DR pensará que la respuesta aceptada es genial. copiar y votar a favor.
Martin Capodici
46

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

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

La respuesta está en dos partes.

En primer lugar, los bytes utilizados internamente por la clase de cadena no importan , y cada vez que suponga que lo hacen, probablemente esté introduciendo un error.

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

Por otro lado, ¿qué sucede si envía estos bytes a un lugar que no puede garantizar que extraerá datos de una secuencia serializada .Net? En este caso, definitivamente debe preocuparse por la codificación, porque obviamente este sistema externo se preocupa. Entonces, nuevamente, los bytes internos utilizados por la cadena no importan: debe elegir una codificación para que pueda ser explícito sobre 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, le digo que no es importante en comparación con asegurarse de que su salida se entienda en el otro extremo y garantizar que debe ser explícito con su codificación. Además, si realmente desea hacer coincidir sus bytes internos, ya puede elegir la Unicodecodificación y obtener ese ahorro de rendimiento.

Lo que me lleva a la segunda parte ... elegir la Unicodecodificación es decirle a .Net que use los bytes subyacentes. Es necesario elegir esta codificación, porque cuando sale un Unicode-Plus nuevo y novedoso, el tiempo de ejecución de .Net debe ser libre de usar este modelo de codificación más nuevo y mejor sin romper el programa. Pero, por el momento (y futuro previsible), solo elegir la codificación Unicode le da lo que desea.

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

Joel Coehoorn
fuente
99
Hay áreas en .NET donde debe obtener conjuntos de bytes para cadenas. Muchas de las clases de criptografía .NET contienen métodos como ComputeHash () que aceptan una matriz de bytes o una secuencia. No tiene otra alternativa que convertir una cadena a una matriz de bytes primero (eligiendo una codificación) y luego, opcionalmente, envolverla en una secuencia. Sin embargo, siempre que elija una codificación (es decir, UTF8) un palo con él, no hay problemas con esto.
Ash
44

Sólo para demostrar que el sonido del Mehrdrad respuesta obras, su enfoque aún pueden persistir los caracteres suplentes no apareados (de los cuales muchos habían formulado contra mi respuesta, pero de los cuales todos son igualmente culpables de, por ejemplo System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; los métodos de codificación no pueden persistir el alto sustituta caracteres, d800por ejemplo, y esos simplemente reemplazan los caracteres 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

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

Cada vez que hay un movimiento en esta pregunta, sigo pensando en un serializador (ya sea de Microsoft o de un componente de terceros) que puede persistir cadenas incluso si contiene caracteres sustitutos no apareados; Google de vez en cuando: serialización de caracteres sustitutos no emparejados .NET . Esto no me hace perder el sueño, pero es un poco molesto cuando de vez en cuando alguien comenta mi respuesta de que es defectuosa, pero sus respuestas son igualmente defectuosas cuando se trata de personajes sustitutos no apareados.

Maldición, Microsoft debería haber usado System.Buffer.BlockCopyen su BinaryFormatter

谢谢!

Michael Buen
fuente
3
¿No tienen que aparecer los sustitutos en pares para formar puntos de código válidos? Si ese es el caso, puedo entender por qué los datos serían destrozados.
dtanders
1
@dtanders Sí, ese es mi pensamiento también, tienen que aparecer en pares, los personajes sustitutos no apareados solo suceden si los pones en secuencia deliberadamente y los haces desaparecer. Lo que no sé es por qué otros desarrolladores siguen insistiendo en que deberíamos usar un enfoque de codificación en su lugar, ya que consideraron que el enfoque de serialización ( mi respuesta , que fue una respuesta aceptada durante más de 3 años) no mantiene el no emparejado personaje sustituto intacto. Pero se olvidaron de verificar que sus soluciones conscientes de la codificación no mantienen el carácter sustituto no emparejado también, la ironía ツ
Michael Buen
Si hay una biblioteca de serialización que usa System.Buffer.BlockCopyinternamente, todos los argumentos de codificación-defensa serán discutibles
Michael Buen
2
@MichaelBuen Me parece que el problema principal es que estás en letras grandes y en negrita diciendo que algo no importa, en lugar de decir que no importa en su caso. Como resultado, está alentando a las personas que miran su respuesta a cometer errores de programación básicos que causarán frustración a otros en el futuro. Los sustitutos no apareados no son válidos en una cadena. No es una matriz de caracteres, por lo que tiene sentido que convertir una cadena a otro formato resulte en un error FFFDen ese carácter. Si desea hacer una manipulación manual de cadenas, use un char [] como se recomienda.
Trisped
2
@dtanders: A System.Stringes una secuencia inmutable de Char; .NET siempre ha permitido Stringconstruir un objeto a partir de cualquiera Char[]y exportar su contenido a uno que Char[]contenga los mismos valores, incluso si el original Char[]contiene sustitutos no emparejados.
supercat
41

Prueba esto, mucho menos código:

System.Text.Encoding.UTF8.GetBytes("TEST String");
Nathan
fuente
¡Entonces prueba esto System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);y llora! Funcionará, pero System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthmientras"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg
99
@ mg30rg: ¿Por qué crees que tu ejemplo es extraño? Seguramente en una codificación de ancho variable no todos los caracteres tienen la misma longitud de byte. ¿Qué tiene de malo?
Vlad
@Vlad Sin embargo, un comentario más válido aquí es que, como símbolos Unicode codificados (por ejemplo, como bytes), los caracteres que incluyen sus propios signos diacríticos darán un resultado diferente que los signos diacríticos divididos en símbolos modificadores agregados al carácter. Pero hay métodos en .net para dividirlos específicamente, para permitir obtener una representación de bytes consistente.
Nyerguds
25

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

Es malo cuando la cadena, por ejemplo, proviene de SQL Server donde se creó a partir de una matriz de bytes que almacena, 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 emparejado 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 un error y es solo una forma, así que lo arreglé y escribí de nuevo 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);
}
Gman
fuente
En lugar de usar su método personalizado para convertir una matriz de bytes a base64, todo lo que tenía que hacer era usar el convertidor incorporado: Convert.ToBase64String (arr);
Makotosan
@Makotosan gracias, pero sí utilicé Convert.ToBase64String(arr); para las conversiones de base64 byte[] (data) <-> string (serialized data to store in XML file). Pero para obtener la inicial byte[] (data), necesitaba hacer algo con un Stringcontenido binario (es la forma en que MSSQL me lo devolvió). SO las funciones anteriores son para String (binary data) <-> byte[] (easy accessible binary data).
Gman
23

También explique por qué se debe tener en cuenta la codificación. ¿No puedo obtener simplemente en qué bytes se ha almacenado la cadena? ¿Por qué esta dependencia de la codificación?

Porque no existe tal cosa como "los bytes de la cadena".

Una cadena (o más genéricamente, un texto) se compone de caracteres: letras, dígitos y otros símbolos. Eso es todo. Las computadoras, sin embargo, no saben nada sobre personajes; solo pueden manejar bytes. Por lo tanto, si desea almacenar o transmitir texto utilizando una computadora, debe transformar los caracteres en bytes. ¿Cómo haces eso? Aquí es donde las codificaciones entran en escena.

Una codificación no es más que una convención para traducir caracteres lógicos a bytes físicos. La codificación más simple y mejor conocida es ASCII, y es todo lo que necesita si escribe en inglés. Para otros idiomas necesitará codificaciones más completas, siendo cualquiera de los sabores Unicode la opción más segura hoy en día.

En resumen, intentar "obtener los bytes de una cadena sin usar codificaciones" es tan imposible como "escribir un texto sin usar ningún idioma".

Por cierto, te recomiendo encarecidamente (y a cualquier persona) que leas esta pequeña pieza de sabiduría: El mínimo absoluto que todo desarrollador de software debe saber absolutamente, positivamente sobre Unicode y conjuntos de caracteres (¡sin excusas!)

Konamiman
fuente
2
Permítanme aclarar: se ha utilizado una codificación para traducir "hola mundo" a bytes físicos. Como la cadena está almacenada en mi computadora, estoy seguro de que debe almacenarse en bytes. Simplemente quiero acceder a esos bytes para guardarlos en el disco o por cualquier otro motivo. No quiero interpretar estos bytes. Como no quiero interpretar estos bytes, la necesidad de una codificación en este punto es tan errónea como requerir una línea telefónica para llamar a printf.
Agnel Kurian
3
Pero, de nuevo, no hay un concepto de traducción de texto a bytes físicos a menos que use una codificación. Claro, el compilador almacena las cadenas de alguna manera en la memoria, pero solo está usando una codificación interna, que usted (o cualquier persona, excepto el desarrollador del compilador) no conoce. Entonces, hagas lo que hagas, necesitas una codificación para obtener bytes físicos de una cadena.
Konamiman
@Agnel Kurian: Por supuesto, es cierto que una cadena tiene un montón de bytes en algún lugar que almacena su contenido (asunto UTF-16). Pero hay una buena razón para evitar que acceda a él: las cadenas son inmutables y si pudiera obtener la matriz interna de bytes [], también podría modificarla. Esto rompe la inmutabilidad, que es vital porque varias cadenas pueden compartir los mismos datos. El uso de una codificación UTF-16 para obtener la cadena probablemente solo copiará los datos.
ollb
2
@Gnafoo, una copia de los bytes servirá.
Agnel Kurian
22

C # para convertir stringa en una bytematriz:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Shyam sundar shah
fuente
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
fuente
Pero, ¿por qué se debe tener en cuenta la codificación? ¿Por qué no puedo obtener los bytes sin tener que ver qué codificación se está utilizando? Incluso si fuera necesario, ¿no debería el objeto String saber qué codificación se está utilizando y simplemente volcar lo que hay en la memoria?
Agnel Kurian
55
Esto no siempre funciona. Algunos caracteres especiales pueden perderse al usar un método que he encontrado por el camino difícil.
JB King
17

Puede usar el siguiente código para la conversión entre cadena y 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);
Jarvis Stark
fuente
VUPeste resolvió mi problema (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

Con la llegada de Span<T>C # 7.2, la técnica canónica para capturar la representación de memoria subyacente de una cadena en una matriz de bytes administrada es:

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

Convertirlo de nuevo no debería ser un comienzo porque eso significa que de hecho estás interpretando los datos de alguna manera, pero en aras de la integridad:

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

Los nombres NonPortableCastyDangerousGetPinnableReference deberían alentar el argumento de que probablemente no debería estar haciendo esto.

Tenga en cuenta que para trabajar Span<T>requiere instalar el paquete System.Memory NuGet .

En cualquier caso, la pregunta original real y los comentarios de seguimiento implican que la memoria subyacente no se está "interpretando" (lo que supongo que significa que no se modifica o lee más allá de la necesidad de escribirla tal cual), lo que indica que alguna implementación de la Streamclase debe usarse en lugar de razonar sobre los datos como cadenas en absoluto.

John Rasch
fuente
13

No estoy seguro, pero creo que la cadena almacena su información como una matriz de caracteres, que es ineficiente con bytes. Específicamente, la definición de un Char es "Representa un carácter Unicode".

tome este ejemplo de muestra:

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

Tenga en cuenta que la respuesta Unicode es de 14 bytes en ambas instancias, mientras que la respuesta UTF-8 es de solo 9 bytes para el primero, y solo 7 para el segundo.

Entonces, si solo desea los bytes utilizados por la cadena, simplemente use Encoding.Unicode, pero será ineficiente con el espacio de almacenamiento.

Ed Marty
fuente
10

La cuestión 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 un mapeo uno a uno a menos que se limite a las cadenas que solo contienen caracteres ASCII. System.Text.Encoding tiene muchas formas de asignar una cadena al byte [], debe elegir una que evite la pérdida de información y que sea fácil de usar para su cliente cuando necesite asignar el byte [] a una cadena .

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

Hans Passant
fuente
3
UTF-8 es compacto solo si la mayoría de sus caracteres están en el conjunto de caracteres inglés (ASCII). Si tuviera una larga cadena de caracteres chinos, UTF-16 sería una codificación más compacta que UTF-8 para esa cadena. Esto se debe a que UTF-8 usa un byte para codificar ASCII y 3 (o tal vez 4) de lo contrario.
Joel Mueller
77
Cierto. Pero, ¿cómo puede no saber acerca de la codificación si está familiarizado con el manejo del texto en chino?
Hans Passant el
9

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
mashet
fuente
OP pide específicamente NO especificar una codificación ... "sin especificar manualmente una codificación específica"
Ferdz
8

La manera más rápida

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

EDITAR como Makotosan comentó que esta es ahora la mejor manera:

Encoding.UTF8.GetBytes(text)
Alessandro Annini
fuente
8
ASCIIEncoding ..... no es necesario. Simplemente se usa Encoding.UTF8.GetBytes (texto)
Makotosan
8

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

Una cuerda en .NET representa 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

Puede usar la respuesta de Mehrdad , pero en realidad usa una codificación porque los caracteres son UTF-16. Llama a ToCharArray que al mirar la fuente crea char[]y copia la memoria directamente. Luego copia los datos a una matriz de bytes que también está asignada. Entonces, bajo el 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 20-30% más rápida que Mehrdad ya que omite el paso intermedio de asignar una matriz de caracteres y copiar los bytes, pero requiere que compile con la /unsafeopción. Si absolutamente 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é se debe tener en cuenta la codificación? ¿No puedo obtener simplemente 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á ingresando datos en su cadena con caracteres no válidos, lo está haciendo mal. Probablemente deberías estar usando una matriz de bytes o una codificación Base64 para comenzar.

Si lo usa System.Text.Encoding.Unicode, su código será más resistente. No tienes que preocuparte por el endianness del sistema en el que se ejecutará su código. No debe preocuparse si la próxima versión del CLR utilizará 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 orden de bytes endian y realizará lo mismo en todos los sistemas, ahora y en el futuro.

Jason Goemaat
fuente
En realidad, una cadena en C # NO está restringida solo a UTF-16. Lo que es cierto es que contiene un vector de unidades de código de 16 bits, pero estas unidades de código de 16 bits no están restringidas a UTF-16 válido. Pero como son de 16 bits, necesita una codificación (orden de bytes) para convertirlos a 8 bits. Una cadena puede almacenar datos no Unicode, incluido el código binario (por ejemplo, una imagen de mapa de bits). Se interpreta como UTF-16 solo en E / S y formateadores de texto que realizan dicha interpretación.
verdy_p
Entonces, en una cadena C #, puede almacenar de forma segura una unidad de código como 0xFFFF o 0xFFFE, incluso si no son caracteres en UTF-16, y puede almacenar un 0xD800 aislado no seguido de una unidad de código en 0xDC00..0xDFFF (es decir sustitutos no apareados que no son válidos en UTF-16). El mismo comentario se aplica a las cadenas en Javascript / ECMAscript y Java.
verdy_p
Cuando utiliza "GetBytes", por supuesto, no especifica una codificación, pero asume un orden de bytes para obtener los dos bytes en una especificación para cada unidad de código almacenada localmente en la cadena. Cuando crea una nueva cadena a partir de bytes, también necesita un convertidor, no necesariamente UTF-8 a UTF-16, puede insertar el 0 adicional en el byte alto o empaquetar dos bytes (en primer orden MSB o primer orden LSB) en la misma unidad de código de 16 bits. Las cadenas son entonces de forma compacta para matrices de enteros de 16 bits. La relación con los "caracteres" es otro problema, en C # no son tipos reales, ya que todavía están representados como cadenas
verdy_p
7

El enfoque más cercano a la pregunta del OP es el de Tom Blodget, que en realidad va al objeto y extrae los bytes. Digo más cercano 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 sí, porque permite almacenar sustitutos no emparejados. Puede 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 es los bytes de cada personaje en una matriz. Y ahí es donde entra la 'codificación'. Por defecto, obtendrá UTF-16LE. Si no le importan los bytes en sí mismos, 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 era la codificación predeterminada, puntos de código, correcciones de errores) , cosas permitidas como sustitutos no apareados, etc.

Pero, ¿por qué dejar la 'codificación' a la magia? ¿Por qué no especifica 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 de cadena. Querías los bytes en los que se ha almacenado la cadena: aquí es donde se hizo la pregunta ingenuamente. Quería los bytes de cadena en una matriz contigua que representa la cadena, y no todos los demás datos binarios que puede contener un objeto de cadena.

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 del objeto de cadena'. Sin embargo, depende de la implementación, y debido a que está mirando las partes internas, puede ser difícil reconstituir una copia de la cadena.

La respuesta de Mehrdad es incorrecta porque es engañosa a nivel conceptual. Aún tiene una lista de bytes, codificada. Su solución particular permite preservar sustitutos no emparejados, esto depende de la implementación. Su solución particular no produciría los bytes de la cadena con precisión si GetBytesdevuelve la cadena en UTF-8 por defecto.


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 carácter tuviera 6 bytes para acomodar el valor utf8 más grande, aún funcionaría. Entonces, de hecho, la codificación del personaje no importa.

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

Gerard ONeill
fuente
Estos caracteres no son compatibles con UTF-8 o UTF-16 o incluso UTF-32 por ejemplo: 񩱠& (Char) 55906& (Char) 55655. Entonces puede estar equivocado y la respuesta de Mehrdad es una conversión segura sin considerar qué tipo de codificaciones se utilizan.
Mojtaba Rezaeian
Raymon, los caracteres ya están representados por algún valor unicode, y todos los valores unicode pueden representarse por todos los utf. ¿Hay una explicación más larga de lo que estás hablando? ¿En qué codificación de caracteres existen esos dos valores (o 3 ..)?
Gerard ONeill
Son caracteres no válidos que no son compatibles con ningún rango de codificación. Esto no significa que sean 100% inútiles. Un código que convierte cualquier tipo de cadena a su equivalente de matriz de bytes independientemente de las codificaciones no es una solución incorrecta y tiene sus propios usos en las ocasiones deseadas.
Mojtaba Rezaeian
1
Ok, entonces creo que no estás entendiendo el problema. Sabemos que es una matriz compatible con Unicode; de ​​hecho, debido a que es .net, sabemos que es UTF-16. Entonces esos personajes no existirán allí. Tampoco leíste completamente mi comentario sobre el cambio de representaciones internas. Una cadena es un objeto, no una matriz de bytes codificada. Así que no estaré de acuerdo con tu última declaración. Desea que el código convierta todas las cadenas unicode a cualquier codificación UTF. Esto hace lo que quieres, correctamente.
Gerard ONeill
Los objetos son secuencia de datos originalmente secuencia de bits que describen un objeto en su estado actual. Por lo tanto, todos los datos en los lenguajes de programación son convertibles en una matriz de bytes (cada byte define 8 bits), ya que es posible que deba mantener algún estado de cualquier objeto en la memoria. Puede guardar y mantener una secuencia de bytes en el archivo o la memoria y convertirla como entero, bigint, imagen, cadena Ascii, cadena UTF-8, cadena encriptada o su propio tipo de datos definido después de leerlo desde el disco. Por lo tanto, no puede decir que los objetos son algo diferente a la secuencia de bytes.
Mojtaba Rezaeian
6

Puede usar el siguiente código para convertir un stringa byte arrayen .NET

string s_unicode = "abcéabc";
byte[] utf8Bytes = System.Text.Encoding.UTF8.GetBytes(s_unicode);
Shyam sundar shah
fuente
3

Si realmente desea una copia de los bytes subyacentes de una cadena, puede usar una función como la que sigue. Sin embargo, no deberías seguir leyendo para averiguar por qué.

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

Esta función le dará una copia de los bytes subyacentes a su cadena, bastante rápido. Obtendrá esos bytes de cualquier manera que estén codificando en su sistema. Es casi seguro que esta codificación es UTF-16LE, pero es un detalle de implementación que no debería preocuparle.

Sería más seguro, más simple y más confiable simplemente llamar,

System.Text.Encoding.Unicode.GetBytes()

Con toda probabilidad, esto dará el mismo resultado, es más fácil de escribir y los bytes siempre serán de ida y vuelta con una llamada a

System.Text.Encoding.Unicode.GetString()
Jodrell
fuente
3

Aquí está mi aplicación poco segura de Stringque Byte[]la conversión:

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

Es mucho más rápido que el de respuesta aceptado, incluso si no es tan elegante como es. Aquí están mis puntos de referencia de cronómetro en más de 10000000 iteraciones:

[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

Para usarlo, debe marcar "Permitir código inseguro" en las propiedades de compilación de su proyecto. Según .NET Framework 3.5, este método también se puede usar como extensión de cadena:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
Tommaso Belluzzo
fuente
¿Es el valor de RuntimeHelpers.OffsetToStringDataun múltiplo de 8 en las versiones de Itanium de .NET? Porque de lo contrario esto fallará debido a las lecturas no alineadas.
Jon Hanna
¿No sería más sencillo invocar memcpy? stackoverflow.com/a/27124232/659190
Jodrell el
2

Simplemente use esto:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
alireza amini
fuente
2
... y pierdo todos los caracteres con un salto superior a 127. En mi lengua materna es perfectamente válido escribir "Árvíztűrő tükörfúrógép". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();devolverá la "Árvizturo tukörfurogép."información perdida que no se puede recuperar. (Y aún no mencioné los idiomas asiáticos donde perderías todos los caracteres.)
mg30rg
2

La cadena se puede convertir en una matriz de bytes de diferentes maneras, debido al siguiente hecho: .NET admite Unicode, y Unicode estandariza varias codificaciones de diferencia llamadas UTF. Tienen diferentes longitudes de representación de bytes, pero son equivalentes en el sentido de que cuando se codifica una cadena, se puede codificar de nuevo a la cadena, pero si la cadena se codifica con un UTF y se decodifica en el supuesto de UTF diferente si se puede atornillar arriba.

Además, .NET admite codificaciones que no son Unicode, pero en general no son válidas (solo serán válidas si se utiliza un subconjunto limitado de puntos de código Unicode en una cadena real, como ASCII). Internamente, .NET admite UTF-16, pero para la representación de flujo, UTF-8 generalmente se usa. También es un estándar de facto para Internet.

No es sorprendente que la clase System.Text.Encoding, que es una clase abstracta, admita la serialización de cadenas en una matriz de bytes y deserialización ; sus clases derivadas admiten codificaciones concretas: ASCIIEncodingy cuatro UTF ( System.Text.UnicodeEncodingadmite UTF-16)

Ref. Este enlace.

Para la serialización a una matriz de bytes usando System.Text.Encoding.GetBytes. Para el uso de la operación inversa System.Text.Encoding.GetChars. Esta función devuelve una matriz de caracteres, por lo que para obtener una cadena, use un constructor de cadenas System.String(char[]).
Ref. Esta página.

Ejemplo:

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? :-)
Vijay Singh Rana
fuente
2

Depende de para qué quieres los bytes

Esto se debe a que, como Tyler dijo acertadamente , "las cadenas no son datos puros. También tienen información ". 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 codificación supuesta 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 apropiada) ya perdió la batalla antes de que comenzara. 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 tiene una cadena de texto con una codificación desconocida

Todos los demás respondieron esta pregunta incorrecta incorrectamente.

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

NUEVA HAMPSHIRE.
fuente
2

Al preguntarle qué piensa hacer con los bytes, respondió :

Lo voy a encriptar. Puedo encriptarlo sin convertirlo, pero todavía me gustaría saber por qué la codificación se juega aquí. Solo dame los bytes es lo que digo.

Independientemente de si tiene la intención de enviar estos datos cifrados a través de la red, volver a cargarlos en la memoria más tarde o enviarlos a otro proceso, tiene la clara intención de descifrarlos en algún momento. En ese caso, la respuesta es que está definiendo un protocolo de comunicación. Un protocolo de comunicación no debe definirse en términos de detalles de implementación de su lenguaje de programación y su tiempo de ejecución asociado. Hay varias razones para esto:

  • Es posible que deba comunicarse con un proceso implementado en un idioma o tiempo de ejecución diferente. (Esto podría incluir un servidor que se ejecuta en otra máquina o que envía la cadena a un cliente de navegador JavaScript, por ejemplo).
  • El programa se puede volver a implementar en otro idioma o tiempo de ejecución en el futuro.
  • La implementación de .NET podría cambiar la representación interna de las cadenas. Puede pensar que esto suena exagerado, pero esto realmente sucedió en Java 9 para reducir el uso de memoria. No hay razón para que .NET no pueda seguir su ejemplo. Skeet sugiere que UTF-16 probablemente no sea óptimo hoy, ya que da lugar al surgimiento de los emoji y otros bloques de Unicode que también necesitan más de 2 bytes para representar, lo que aumenta la probabilidad de que la representación interna pueda cambiar en el futuro.

Para comunicarse (ya sea con un proceso completamente diferente o con el mismo programa en el futuro), debe definir su protocolo estrictamente para minimizar la dificultad de trabajar con él o crear errores accidentalmente. Dependiendo de la representación interna de .NET, no es una definición estricta, clara o incluso garantizada como consistente. Una codificación estándar es una definición estricta que no le fallará en el futuro.

En otras palabras, no puede satisfacer su requisito de coherencia sin especificar una codificación.

Ciertamente, puede optar por usar UTF-16 directamente si encuentra que su proceso funciona significativamente mejor ya que .NET lo usa internamente o por cualquier otra razón, pero debe elegir esa codificación explícitamente y realizar esas conversiones explícitamente en su código en lugar de depender en la implementación interna de .NET.

Así que elige una codificación y úsala:

using System.Text;

// ...

Encoding.Unicode.GetBytes("abc"); # UTF-16 little endian
Encoding.UTF8.GetBytes("abc")

Como puede ver, en realidad también es menos código usar los objetos de codificación integrados que implementar sus propios métodos de lector / escritor.

jpmc26
fuente
1

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 inferior con más frecuencia que el superior, no los he comparado con la velocidad.


fuente
44
¿Qué pasa con los caracteres multibyte?
Agnel Kurian
c. ToByte () es privado: S
Khodor
@AgnelKurian Msdn dice "Este método devuelve un valor de byte sin signo que representa el código numérico del objeto Char que se le pasó. En .NET Framework, un objeto Char es un valor de 16 bits. Esto significa que el método es adecuado para devolver "los códigos numéricos de caracteres en el rango de caracteres ASCII o en los controles Unicode C0 y Basic Latin, y los controles C1 y los rangos de Suplemento Latin-1, de U + 0000 a U + 00FF".
mg30rg
1
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
usuario1120193
fuente