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




variable de tipo string c# (20)

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

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

Una cadena (o más genéricamente, un texto) está compuesta de caracteres: letras, dígitos y otros símbolos. Eso es todo. Las computadoras, sin embargo, no saben nada de los personajes; solo pueden manejar bytes. Por lo tanto, si desea almacenar o transmitir texto usando una computadora, necesita transformar los caracteres en bytes. ¿Cómo haces eso? Aquí es donde las codificaciones vienen a la 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 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 de Unicode la opción más segura en la actualidad.

Entonces, en pocas palabras, tratar de "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 a ti (y a cualquier persona) que leas esta pequeña pieza de sabiduría: joelonsoftware.com/articles/Unicode.html

https://code.i-harness.com

¿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?


¿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.


Al contrario de 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, honestamente no entiendo por qué la gente te dice que necesitas las codificaciones. Ciertamente, NO es necesario 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;
}

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 tu programa (u otros programas) no intente interpretar los bytes de alguna manera, lo que obviamente no mencionaste que pretendes hacer, ¡entonces no hay nada de malo en este enfoque! Preocuparse por las codificaciones solo hace que tu vida sea más complicada sin ninguna razón real.

Beneficio adicional a este enfoque:

¡No importa si la cadena contiene caracteres no válidos, porque de todos modos aún 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 usó una codificación específica, le habría dado problemas para codificar / descodificar caracteres no válidos.


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

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.


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.


Intenta esto, mucho menos código:

System.Text.Encoding.UTF8.GetBytes("TEST String");

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.


Solo para demostrar que la share sonido de Mehrdrad funciona, su enfoque puede incluso persistir en [BinaryFormatter (de los cuales muchos se habían nivelado en contra de mi respuesta, pero de los cuales todos son igualmente culpables, por ejemplo, System.Text.Encoding.UTF8.GetBytes , System.Text.Encoding.Unicode.GetBytes ; esos métodos de codificación no pueden conservar los caracteres sustitutos altos d800 por ejemplo, y simplemente reemplazan los caracteres sustitutos altos con el 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

Intente eso con System.Text.Encoding.UTF8.GetBytes o System.Text.Encoding.Unicode.GetBytes , simplemente reemplazarán los caracteres sustitutos altos con el 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 en cadenas incluso que contiene caracteres sustitutos no pareados; Busco en Google esto de vez en cuando: serialización no pareada . Surrogate caracter .NET . Esto no me hace perder el sueño, pero es un poco molesto cuando de vez en cuando alguien comenta en mi respuesta que tiene fallas, pero sus respuestas son igualmente erróneas cuando se trata de personajes sustitutos no apareados.

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

谢谢!


La manera más rápida

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

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

Encoding.UTF8.GetBytes(text)

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

Simplemente usa esto:

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

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 la anwser aceptada, aunque no sea tan elegante como es. Aquí están mis puntos de referencia de cronómetro sobre 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 no seguro" en las propiedades de compilación de su proyecto. Según .NET Framework 3.5, este método también se puede utilizar como extensión de cadena:

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

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

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'?


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.


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

Toma este ejemplo de ejemplo:

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 de Unicode es de 14 bytes en ambos casos, mientras que la respuesta de UTF-8 es solo de 9 bytes para el primero, y solo de 7 para el segundo.

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


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

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

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

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

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

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

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

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

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

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

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

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

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




character-encoding