Conversión de cadena a matriz de bytes en C #

670

Estoy convirtiendo algo de VB a C #. Tener un problema con la sintaxis de esta declaración:

if ((searchResult.Properties["user"].Count > 0))
{
    profile.User = System.Text.Encoding.UTF8.GetString(searchResult.Properties["user"][0]);
}

Entonces veo los siguientes errores:

Argumento 1: no se puede convertir de 'objeto' a 'byte []'

La mejor coincidencia de método sobrecargado para 'System.Text.Encoding.GetString (byte [])' tiene algunos argumentos no válidos

Traté de arreglar el código basado en esta publicación, pero aún no tuve éxito

string User = Encoding.UTF8.GetString("user", 0);

¿Alguna sugerencia?

hora de la fiesta
fuente
1
¿De qué tipo es searchResult.Properties["user"][0]? Intenta byte[]
lanzarlo
mshsayem fue a donde yo iba. ¿Echas de menos un reparto a un (byte[])en el resultado de búsqueda?
Harrison
2
Necesita saber de qué tipo Properties["user"][0]es. Si está seguro de que es una matriz de bytes, puede emitir asíprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP
1
Resulta que no había necesidad de tanto alboroto. El nombre de usuario podría recuperarse sin codificación después de todo.
nouptime
3
¿Por qué no seleccionas la respuesta verdadera?
Ali

Respuestas:

1189

Si ya tiene una matriz de bytes, necesitará saber qué tipo de codificación se utilizó para convertirla en esa matriz de bytes.

Por ejemplo, si la matriz de bytes se creó así:

byte[] bytes = Encoding.ASCII.GetBytes(someString);

Tendrá que volver a convertirlo en una cadena como esta:

string someString = Encoding.ASCII.GetString(bytes);

Si puede encontrar en el código que heredó, la codificación utilizada para crear la matriz de bytes debe estar configurada.

Timothy Randall
fuente
3
Timothy, he revisado el código VB y parece que no puedo encontrar una matriz de bytes como mencionaste.
nouptime
En su resultado de búsqueda, ¿cuál es el tipo de la propiedad Propiedades?
Timothy Randall
Todo lo que puedo ver es que hay un número de elementos adjuntos a las Propiedades como una cadena. Sin embargo, no estoy seguro de si eso es lo que me preguntabas.
nouptime
16
@AndiAR pruebe Encoding.UTF8.GetBytes (somestring)
OzBob
1
Para mi situación, descubrí que Encoding.Unicode.GetBytes funcionaba (pero ASCII no)
Jeff
106

En primer lugar, agregue el System.Textespacio de nombres

using System.Text;

Entonces usa este código

string input = "some text"; 
byte[] array = Encoding.ASCII.GetBytes(input);

¡Espero arreglarlo!

Shridhar
fuente
42

También puede usar un Método de extensión para agregar un método al stringtipo de la siguiente manera:

static class Helper
{
   public static byte[] ToByteArray(this string str)
   {
      return System.Text.Encoding.ASCII.GetBytes(str);
   }
}

Y úsalo como a continuación:

string foo = "bla bla";
byte[] result = foo.ToByteArray();
Ali
fuente
12
Cambiaría el nombre de ese método para incluir el hecho de que está usando codificación ASCII. Algo así como ToASCIIByteArray. Odio cuando descubro que una biblioteca que estoy usando usa ASCII y asumo que está usando UTF-8 o algo más moderno.
T Blank
30
var result = System.Text.Encoding.Unicode.GetBytes(text);
Kuganrajh Rajendran
fuente
3
Esta debería ser la respuesta aceptada, ya que las otras respuestas sugieren ASCII, pero la codificación es Unicode (que es UTF16) o UTF8.
Abel
26
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);
}
Eran Yogev
fuente
Esto fallará para los personajes que caen en el rango de pares sustitutos. GetBytes tendrá una matriz de bytes que pierde un carácter normal por par sustituto al final. GetString tendrá caracteres vacíos al final. La única forma en que funcionaría sería si el valor predeterminado de microsoft fuera UTF32, o si no se permitieran los caracteres en el rango de pares sustitutos. ¿O hay algo que no estoy viendo? La forma correcta es 'codificar' la cadena en bytes.
Gerard ONeill
Correcto, para un rango más amplio puede usar algo similar a la solución de #Timothy Randall: usar System; usando System.Text; Ejemplo de espacio de nombres {programa de clase pública {public static void Main (string [] args) {string s1 = "Hello World"; cadena s2 = "שלום עולם"; cadena s3 = "你好 , 世界!"; Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s1))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s2))); Console.WriteLine (Encoding.UTF8.GetString (Encoding.UTF8.GetBytes (s3))); }}}
Eran Yogev
17

¿Por qué no se debe usar Encoding.Default?

La respuesta de @ Randall utiliza Encoding.Default, sin embargo, Microsoft hace una advertencia en su contra :

Diferentes computadoras pueden usar diferentes codificaciones como predeterminadas, y la codificación predeterminada puede cambiar en una sola computadora. Si utiliza la codificación predeterminada para codificar y decodificar datos transmitidos entre computadoras o recuperados en diferentes momentos en la misma computadora, puede traducir esos datos incorrectamente. Además, la codificación devuelta por la propiedad Predeterminada utiliza el mejor respaldo para asignar caracteres no admitidos a caracteres admitidos por la página de códigos. Por estos motivos, no se recomienda utilizar la codificación predeterminada. Para garantizar que los bytes codificados se decodifiquen correctamente, debe usar una codificación Unicode, como UTF8Encoding o UnicodeEncoding. También puede usar un protocolo de nivel superior para garantizar que se use el mismo formato para codificar y decodificar.

Para verificar cuál es la codificación predeterminada, use Encoding.Default.WindowsCodePage(1250 en mi caso, y lamentablemente, no hay una clase predefinida de codificación CP1250, pero el objeto podría recuperarse como Encoding.GetEncoding(1250)).

Encoding.ASCII es de 7 bits, por lo que tampoco funciona, en mi caso:

byte[] pass = Encoding.ASCII.GetBytes("šarže");
Console.WriteLine(Encoding.ASCII.GetString(pass)); // ?ar?e

... y por qué la codificación UTF-8 debería usarse en su lugar ...

La codificación predeterminada es engañosa: .NET utiliza UTF-8 en todas partes como el valor predeterminado real (las codificaciones de 8 bits se volvieron obsoletas a finales de 20. siglo, marque ie Console.OutputEncoding.EncodingName*) por lo que cada constante que defina en el código está codificada de forma predeterminada UTF-8, por lo que este debe usarse a menos que la fuente de datos tenga una codificación diferente.

* Este es UTF-8 en mi caso, lo cual es una mentira directa: chcpdesde la consola de Windows (cmd) devuelve 852, y esto no debe cambiarse, porque los comandos del sistema localizados (como ping) tienen esta página de códigos codificada

Siguiendo la recomendación de Microsoft:

var utf8 = new UTF8Encoding();
byte[] pass = utf8.GetBytes("šarže");
Console.WriteLine(utf8.GetString(pass)); // šarže

Encoding.UTF8 recomendado por otros es una instancia de codificación UTF-8 y también se puede usar directamente o como

var utf8 = Encoding.UTF8 as UTF8Encoding;

... pero no se usa siempre

La codificación de los conjuntos de bytes debería "funcionar" en Unicode en los países occidentales, pero tan pronto como mueva su programa a algunas regiones menos compatibles (como aquí en Europa del Este), es un verdadero desastre: en la República Checa, el uso predeterminado de Windows (¡en 2020!) MS no estándar 852 (también conocido como Latin-2) para consola, 1250 como OEM de Windows, UTF-8 (65001) como .NET (y otros) nuevo valor predeterminado y debemos tener en cuenta que algunos 8 bits de la UE occidental los datos todavía están en 1252, mientras que el antiguo estándar occidental de 8 bits para Europa del Este era ISO-8859-2 (también conocido como Latin-2, pero NO el mismo Latin-2 que 852). Usar ASCII significa texto lleno de tofu y '?' aquí. Entonces, hasta la mitad del siglo XXI, configure UTF-8 explícitamente .

Jan Turoň
fuente
12

A partir de la respuesta de Ali , recomendaría un método de extensión que le permita pasar opcionalmente la codificación que desea usar:

using System.Text;
public static class StringExtensions
{
    /// <summary>
    /// Creates a byte array from the string, using the 
    /// System.Text.Encoding.Default encoding unless another is specified.
    /// </summary>
    public static byte[] ToByteArray(this string str, Encoding encoding = Encoding.Default)
    {
        return encoding.GetBytes(str);
    }
}

Y úsalo como a continuación:

string foo = "bla bla";

// default encoding
byte[] default = foo.ToByteArray();

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);
Dan Sinclair
fuente
2
Tenga en cuenta que el uso de Encoding encoding = Encoding.Defaultlos resultados en un error de tiempo de compilación:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell
11

El siguiente enfoque funcionará solo si los caracteres son de 1 byte. (Unicode predeterminado no funcionará ya que es de 2 bytes)

public static byte[] ToByteArray(string value)
{            
    char[] charArr = value.ToCharArray();
    byte[] bytes = new byte[charArr.Length];
    for (int i = 0; i < charArr.Length; i++)
    {
        byte current = Convert.ToByte(charArr[i]);
        bytes[i] = current;
    }

    return bytes;
}

Manteniéndolo simple

Mandar Sudame
fuente
chary stringson UTF-16 por definición.
Tom Blodget
Sí, el valor predeterminado es UTF-16. No estoy haciendo suposiciones sobre la codificación de la cadena de entrada.
Mandar Sudame
No hay texto sino texto codificado. Su entrada es tipo stringy, por lo tanto, es UTF-16. UTF-16 no es el predeterminado; No hay elección al respecto. Luego se divide en char[]unidades de código UTF-16. Luego llama a Convert.ToByte (Char) , que simplemente convierte U + 0000 a U + 00FF a ISO-8859-1 , y destruye cualquier otro punto de código.
Tom Blodget
Tiene sentido. Gracias por la aclaración. Actualizando mi respuesta.
Mandar Sudame
1
Creo que todavía te faltan varios puntos esenciales. Concéntrese en chartener 16 bits y Convert.ToByte()tirar la mitad de ellos.
Tom Blodget
10

utilizar este

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
alireza amini
fuente
6

Un refinamiento a la edición de JustinStolle (el uso de BlockCopy por parte de Eran Yogev).

La solución propuesta es de hecho más rápida que usar la codificación. El problema es que no funciona para codificar conjuntos de bytes de longitud desigual. Según lo dado, plantea una excepción fuera de límite. Aumentar la longitud en 1 deja un byte final al decodificar desde una cadena.

Para mí, la necesidad vino cuando quería codificar de DataTablea JSON. Estaba buscando una manera de codificar campos binarios en cadenas y decodificar de cadena a byte[].

Por lo tanto, creé dos clases: una que envuelve la solución anterior (cuando se codifica desde cadenas está bien, porque las longitudes son siempre iguales), y otra que maneja byte[] codificación.

Resolví el problema de longitud desigual agregando un solo carácter que me indica si la longitud original de la matriz binaria era impar ('1') o par ('0')

Como sigue:

public static class StringEncoder
{
    static byte[] EncodeToBytes(string str)
    {
        byte[] bytes = new byte[str.Length * sizeof(char)];
        System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
        return bytes;
    }
    static string DecodeToString(byte[] bytes)
    {
        char[] chars = new char[bytes.Length / sizeof(char)];
        System.Buffer.BlockCopy(bytes, 0, chars, 0, bytes.Length);
        return new string(chars);
    }
}

public static class BytesEncoder
{
    public static string EncodeToString(byte[] bytes)
    {
        bool even = (bytes.Length % 2 == 0);
        char[] chars = new char[1 + bytes.Length / sizeof(char) + (even ? 0 : 1)];
        chars[0] = (even ? '0' : '1');
        System.Buffer.BlockCopy(bytes, 0, chars, 2, bytes.Length);

        return new string(chars);
    }
    public static byte[] DecodeToBytes(string str)
    {
        bool even = str[0] == '0';
        byte[] bytes = new byte[(str.Length - 1) * sizeof(char) + (even ? 0 : -1)];
        char[] chars = str.ToCharArray();
        System.Buffer.BlockCopy(chars, 2, bytes, 0, bytes.Length);

        return bytes;
    }
}
usuario4726577
fuente
4

Esta pregunta ha sido respondida muchas veces, pero con C # 7.2 y la introducción del tipo Span, hay una forma más rápida de hacer esto en código inseguro:

public static class StringSupport
{
    private static readonly int _charSize = sizeof(char);

    public static unsafe byte[] GetBytes(string str)
    {
        if (str == null) throw new ArgumentNullException(nameof(str));
        if (str.Length == 0) return new byte[0];

        fixed (char* p = str)
        {
            return new Span<byte>(p, str.Length * _charSize).ToArray();
        }
    }

    public static unsafe string GetString(byte[] bytes)
    {
        if (bytes == null) throw new ArgumentNullException(nameof(bytes));
        if (bytes.Length % _charSize != 0) throw new ArgumentException($"Invalid {nameof(bytes)} length");
        if (bytes.Length == 0) return string.Empty;

        fixed (byte* p = bytes)
        {
            return new string(new Span<char>(p, bytes.Length / _charSize));
        }
    }
}

Tenga en cuenta que los bytes representan una cadena codificada UTF-16 (llamada "Unicode" en C # land).

Algunas evaluaciones comparativas rápidas muestran que los métodos anteriores son aproximadamente 5 veces más rápidos que sus implementaciones Encoding.Unicode.GetBytes (...) / GetString (...) para cadenas de tamaño mediano (30-50 caracteres), e incluso más rápido para cadenas más grandes. Estos métodos también parecen ser más rápidos que usar punteros con Marshal.Copy (..) o Buffer.MemoryCopy (...).

Algemist
fuente
4

Si el resultado de 'searchResult.Properties ["user"] [0]', es una cadena:

if ( ( searchResult.Properties [ "user" ].Count > 0 ) ) {

   profile.User = System.Text.Encoding.UTF8.GetString ( searchResult.Properties [ "user" ] [ 0 ].ToCharArray ().Select ( character => ( byte ) character ).ToArray () );

}

El punto clave es que la conversión de una cadena a un byte [] se puede hacer usando LINQ:

.ToCharArray ().Select ( character => ( byte ) character ).ToArray () )

Y lo contrario:

.Select ( character => ( char ) character ).ToArray () )
Janus
fuente
3

¿Alguien ve alguna razón por la que no hacer esto?

mystring.Select(Convert.ToByte).ToArray()
Lomithrani
fuente
10
Convert.ToByte(char)no funciona como crees que lo haría. El carácter '2'se convierte al byte 2, no al byte que representa el carácter '2'. Usar en su mystring.Select(x => (byte)x).ToArray()lugar.
Jack
3

Esto me funcionó

byte[] bytes = Convert.FromBase64String(textString);
Mina Matta
fuente
eso solo funciona cuando su cadena solo contiene az, AZ, 0-9, +, /. No se permiten otros personajes de.wikipedia.org/wiki/Base64
Blechdose
2

Puede usar la API MemoryMarshal para realizar una conversión muy rápida y eficiente. Stringse convertirá implícitamente en ReadOnlySpan<byte>, ya MemoryMarshal.Castsea como acepta Span<byte>o ReadOnlySpan<byte>como parámetro de entrada.

public static class StringExtensions
{
    public static byte[] ToByteArray(this string s) => s.ToByteSpan().ToArray(); //  heap allocation, use only when you cannot operate on spans
    public static ReadOnlySpan<byte> ToByteSpan(this string s) => MemoryMarshal.Cast<char, byte>(s);
}

El siguiente punto de referencia muestra la diferencia:

Input: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"

|                       Method |       Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
|----------------------------- |-----------:|----------:|----------:|-------:|------:|------:|----------:|
| UsingEncodingUnicodeGetBytes | 160.042 ns | 3.2864 ns | 6.4099 ns | 0.0780 |     - |     - |     328 B |
| UsingMemoryMarshalAndToArray |  31.977 ns | 0.7177 ns | 1.5753 ns | 0.0781 |     - |     - |     328 B |
|           UsingMemoryMarshal |   1.027 ns | 0.0565 ns | 0.1630 ns |      - |     - |     - |         - |
Pawel Maga
fuente
0

Esto funcionó para mí, después de eso pude convertir poner mi imagen en un campo bytea en mi base de datos.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
usuario10863293
fuente