Convertendo string em matriz de bytes em C #


670

Estou convertendo algo do VB para C #. Tendo um problema com a sintaxe desta declaração:

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

Eu vejo os seguintes erros:

Argumento 1: não é possível converter de 'objeto' para 'byte []'

A melhor correspondência de método sobrecarregado para 'System.Text.Encoding.GetString (byte [])' possui alguns argumentos inválidos

Tentei corrigir o código com base nesta postagem, mas ainda não obtive sucesso

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

Alguma sugestão?


1
Qual é o tipo de searchResult.Properties["user"][0]? Tente byte[]
convertê-

mshsayem foi para onde eu estava indo. Está faltando um elenco para um (byte[])no searchResult?
Harrison

2
Você precisa descobrir qual Properties["user"][0]é o tipo . Se você tem certeza de que é uma matriz de bytes, pode transmitir assimprofile.User = System.Text.Encoding.UTF8.GetString((byte[])searchResult.Properties["user"][0]);
keyboardP:

1
Acontece que não havia necessidade de tanta confusão. O nome de usuário pode ser buscado sem codificação, afinal.
Nouptime 14/03/14

3
Por que você não seleciona a resposta verdadeira?
Ali

Respostas:


1189

Se você já possui uma matriz de bytes, precisará saber que tipo de codificação foi usada para inseri-la nessa matriz de bytes.

Por exemplo, se a matriz de bytes foi criada assim:

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

Você precisará transformá-lo novamente em uma string como esta:

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

Se você pode encontrar no código que herdou, a codificação usada para criar a matriz de bytes, então você deve estar definido.


3
Timothy, examinei o código VB e não consigo encontrar uma matriz de bytes como você mencionou.
nouptime

No seu resultado de pesquisa, qual é o tipo da propriedade Propriedades?
Timothy Randall

Tudo o que vejo é que há um número de itens anexados às Propriedades como uma sequência. Não tenho certeza se é isso que você estava me perguntando.
nouptime

16
@AndiAR try Encoding.UTF8.GetBytes (somestring)
OzBob

1
Para minha situação eu achei que Encoding.Unicode.GetBytes trabalhou (mas ASCII não)
Jeff

106

Primeiro de tudo, adicione o System.Textespaço para nome

using System.Text;

Então use este código

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

Espero consertar!


42

Além disso, você pode usar um método de extensão para adicionar um método ao stringtipo como abaixo:

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

E use-o como abaixo:

string foo = "bla bla";
byte[] result = foo.ToByteArray();

12
Renomeio esse método para incluir o fato de que ele está usando codificação ASCII. Algo como ToASCIIByteArray. Eu odeio quando descubro que alguma biblioteca que estou usando usa ASCII e suponho que esteja usando UTF-8 ou algo mais moderno.
T em branco

30
var result = System.Text.Encoding.Unicode.GetBytes(text);

3
Essa deve ser a resposta aceita, pois as outras respostas sugerem ASCII, mas a codificação é Unicode (que UTF16) ou 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);
}

Isso falhará para caracteres que se enquadram no intervalo de pares substitutos. GetBytes terá uma matriz de bytes que perderá um caractere normal por par substituto no final. O GetString terá caracteres vazios no final. A única maneira de funcionar é se o padrão da Microsoft for UTF32 ou se caracteres no intervalo de pares substitutos não forem permitidos. Ou há algo que eu não estou vendo? A maneira correta é 'codificar' a string em bytes.
precisa

Correto, para uma faixa mais ampla, você pode usar algo semelhante à solução de # Timothy Randall: using System; using System.Text; namespace Exemplo {public class Program {public static void Principal (string [] args) {string s1 = "Olá Mundo"; string s2 = "שלום עולם"; string 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 que Encoding.Default não deve ser usado ...

A resposta de @ Randall é usada Encoding.Default, no entanto, a Microsoft lança um aviso contra :

Computadores diferentes podem usar codificações diferentes como padrão e a codificação padrão pode ser alterada em um único computador. Se você usar a codificação padrão para codificar e decodificar dados transmitidos entre computadores ou recuperados em momentos diferentes no mesmo computador, isso poderá traduzir esses dados incorretamente. Além disso, a codificação retornada pela propriedade Padrão usa o fallback de melhor ajuste para mapear caracteres não suportados para caracteres suportados pela página de código. Por esses motivos, o uso da codificação padrão não é recomendado. Para garantir que os bytes codificados sejam decodificados corretamente, você deve usar uma codificação Unicode, como UTF8Encoding ou UnicodeEncoding. Você também pode usar um protocolo de nível superior para garantir que o mesmo formato seja usado para codificação e decodificação.

Para verificar qual é a codificação padrão, use Encoding.Default.WindowsCodePage(1250 no meu caso - e, infelizmente, não há classe predefinida de codificação CP1250, mas o objeto pode ser recuperado comoEncoding.GetEncoding(1250) ).

Encoding.ASCII é 7bit, então também não funciona, no meu caso:

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

... e por que a codificação UTF-8 deve ser usada ...

A codificação padrão é enganosa: o .NET usa UTF-8 em todos os lugares como o padrão real (codificações de 8 bits se tornaram obsoletas no final do século 20. verifique Console.OutputEncoding.EncodingName *) para que todas as constantes definidas no código sejam codificadas em UTF-8 por padrão - portanto este deve ser usado, a menos que a fonte de dados esteja em codificação diferente.

* Este é UTF-8 no meu caso, o que é uma mentira direta: chcp do console do Windows (cmd) retorna 852 - e isso não deve ser alterado, porque os comandos do sistema localizado (como ping) possuem essa página de código codificada

Seguindo a recomendação da Microsoft:

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

Encoding.UTF8 recomendado por outras pessoas é uma instância de codificação UTF-8 e também pode ser usado diretamente ou como

var utf8 = Encoding.UTF8 as UTF8Encoding;

... mas nem sempre é usado

A codificação para matrizes de bytes deve "apenas funcionar" em Unicode nos países ocidentais, mas assim que você move o programa para algumas regiões com menos suporte (como aqui na Europa Oriental), é uma verdadeira bagunça: na República Tcheca, os padrões do Windows usam (em 2020!) MS não-padrão 852 (também conhecido como Latin-2) para console, 1250 como Windows OEM, UTF-8 (65001) como .NET (e outros) novo padrão e devemos ter em mente que alguns países de 8 bits da UE ocidental os dados ainda estão em 1252, enquanto o antigo padrão ocidental de 8 bits da Europa Oriental era ISO-8859-2 (também conhecido como Latin-2, mas NÃO o mesmo Latin-2 que 852). Usar ASCII significa texto cheio de tofu e '?' aqui. Portanto, até a metade do século XXI, defina UTF-8 explicitamente .


12

Com base na resposta de Ali , eu recomendaria um método de extensão que permita passar opcionalmente a codificação que você deseja 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);
    }
}

E use-o como abaixo:

string foo = "bla bla";

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

// custom encoding
byte[] unicode = foo.ToByteArray(Encoding.Unicode);

2
Observe que usar Encoding encoding = Encoding.Defaultresultados em um erro de tempo de compilação:CS1736 Default parameter value for 'encoding' must be a compile-time constant
Douglas Gaskell

11

A abordagem a seguir funcionará apenas se os caracteres tiverem 1 byte. (O unicode padrão não funcionará, pois tem 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;
}

Mantendo as coisas simples


chare stringsão UTF-16 por definição.
precisa

Sim, o padrão é UTF-16. Não estou fazendo nenhuma suposição sobre a codificação da string de entrada.
Mandar Sudame

Não há texto, mas texto codificado. Sua entrada é do tipo stringe, portanto, é UTF-16. UTF-16 não é o padrão; não há escolha sobre isso. Em seguida, você divide em char[]unidades de código UTF-16. Você então chama Convert.ToByte (Char) , que por acaso converte U + 0000 em U + 00FF em ISO-8859-1 e gerencia outros pontos de código.
precisa

Faz sentido. Obrigado pelo esclarecimento. Atualizando minha resposta.
Mandar Sudame

1
Eu acho que você ainda está perdendo vários pontos essenciais. Concentre-se em charter 16 bits e Convert.ToByte()jogar metade deles fora.
TomTodododget #

10

usa isto

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

6

Um refinamento para a edição de JustinStolle (uso de BlockCopy por Eran Yogev).

A solução proposta é realmente mais rápida do que usar Encoding. O problema é que ele não funciona para codificar matrizes de bytes de comprimento desigual. Como dado, gera uma exceção fora dos limites. Aumentar o comprimento em 1 deixa um byte à direita ao decodificar da string.

Para mim, surgiu a necessidade quando eu queria codificar de DataTablepara JSON. Eu estava procurando uma maneira de codificar campos binários em strings e decodificar da string de volta para byte[].

Portanto, criei duas classes - uma que envolve a solução acima (ao codificar a partir de seqüências de caracteres, tudo bem, porque os comprimentos são sempre pares) e outra que lida com a byte[]codificação.

Resolvi o problema de comprimento irregular adicionando um único caractere que informa se o comprimento original da matriz binária era ímpar ('1') ou par ('0')

Do seguinte modo:

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

4

Esta pergunta foi respondida várias vezes, mas com o C # 7.2 e a introdução do tipo Span, há uma maneira mais rápida de fazer isso no código não seguro:

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

Lembre-se de que os bytes representam uma sequência codificada em UTF-16 (chamada "Unicode" em C # land).

Alguns testes rápidos mostram que os métodos acima são aproximadamente 5x mais rápidos que suas implementações Encoding.Unicode.GetBytes (...) / GetString (...) para cadeias de tamanho médio (30 a 50 caracteres) e ainda mais rápidas para cadeias de caracteres maiores. Esses métodos também parecem ser mais rápidos do que usar ponteiros com Marshal.Copy (..) ou Buffer.MemoryCopy (...).


4

Se o resultado de 'searchResult.Properties ["user"] [0]', for uma sequência:

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

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

}

O ponto principal é que a conversão de uma string em um byte [] pode ser feita usando o LINQ:

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

E o inverso:

.Select ( character => ( char ) character ).ToArray () )

3

Alguém vê alguma razão para não fazer isso?

mystring.Select(Convert.ToByte).ToArray()

10
Convert.ToByte(char)não funciona como você pensa. O caractere '2'é convertido no byte 2, não no byte que representa o caractere '2'. Use em mystring.Select(x => (byte)x).ToArray()vez disso.
Jack


2

Você pode usar a API MemoryMarshal para realizar uma conversão muito rápida e eficiente. Stringserá implicitamente convertido para ReadOnlySpan<byte>, como MemoryMarshal.Castaceita um Span<byte>ou ReadOnlySpan<byte>como um 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);
}

O benchmark a seguir mostra a diferença:

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

0

Este trabalho para mim, depois disso eu pude converter colocar minha foto em um campo bytea no meu banco de dados.

using (MemoryStream s = new MemoryStream(DirEntry.Properties["thumbnailphoto"].Value as byte[]))
{
    return s.ToArray();
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.