Como obtenho uma representação de bytes consistente de seqüências de caracteres em C # sem especificar manualmente uma codificação?

2190

Como converter um stringpara um byte[]no .NET (C #) sem especificar manualmente uma codificação específica?

Vou criptografar a string. Posso criptografá-lo sem convertê-lo, mas eu ainda gostaria de saber por que a codificação chega aqui.

Além disso, por que a codificação deve ser levada em consideração? Não posso simplesmente obter em que bytes a cadeia foi armazenada? Por que existe uma dependência nas codificações de caracteres?

Agnel Kurian
fonte
23
Cada string é armazenada como uma matriz de bytes, certo? Por que não posso simplesmente ter esses bytes?
Agnel Kurian
135
A codificação é o que mapeia os caracteres para os bytes. Por exemplo, em ASCII, a letra 'A' é mapeada para o número 65. Em uma codificação diferente, pode não ser a mesma. A abordagem de alto nível para as seqüências de caracteres adotadas na estrutura .NET torna isso bastante irrelevante, no entanto (exceto neste caso).
Lucas Jones
20
Para interpretar o advogado do diabo: Se você deseja obter os bytes de uma string na memória (como o .NET os usa) e manipulá-los de alguma forma (por exemplo, CRC32), e NUNCA NUNCA quis decodificá-lo novamente na string original ... Não é simples explicar por que você se importaria com codificações ou como escolher qual usar.
Greg
78
Surpreendeu ninguém deu este link ainda: joelonsoftware.com/articles/Unicode.html
Bevan
28
Um caractere não é um byte e um byte não é um caractere. Um caractere é a chave para uma tabela de fontes e uma tradição lexical. Uma string é uma sequência de caracteres. (Palavras, parágrafos, sentenças e títulos também têm suas próprias tradições lexicais que justificam suas próprias definições de tipo - mas discordo). Como números inteiros, números de ponto flutuante e tudo mais, os caracteres são codificados em bytes. Houve um tempo em que a codificação era simples de um para um: ASCII. No entanto, para acomodar toda a simbologia humana, as 256 permutações de um byte eram insuficientes e as codificações foram criadas para usar seletivamente mais bytes.
George

Respostas:

1855

Ao contrário das respostas aqui, você NÃO precisa se preocupar com a codificação se os bytes não precisarem ser interpretados!

Como você mencionou, seu objetivo é simplesmente "obter em que bytes a cadeia foi armazenada" .
(E, é claro, para poder reconstruir a sequência a partir dos bytes.)

Para esses objetivos, sinceramente não entendo por que as pessoas continuam dizendo que você precisa das codificações. Você certamente NÃO precisa se preocupar com codificações para isso.

Basta fazer isso:

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

Contanto que seu programa (ou outros programas) não tente interpretar os bytes de alguma forma, o que você obviamente não mencionou que pretende fazer, então não há nada de errado com essa abordagem! Preocupar-se com as codificações apenas torna sua vida mais complicada sem motivo real.

Benefício adicional para esta abordagem:

Não importa se a string contém caracteres inválidos, porque você ainda pode obter os dados e reconstruir a string original de qualquer maneira!

Ele será codificado e decodificado da mesma forma, porque você está apenas olhando os bytes .

Se você usasse uma codificação específica, isso lhe causaria problemas com a codificação / decodificação de caracteres inválidos.

user541686
fonte
247
O que é feio sobre este é, que GetStringe GetBytesnecessidade de executado em um sistema com o mesmo endianness ao trabalho. Portanto, você não pode usar isso para obter bytes que deseja transformar em uma string em outro lugar. Por isso, tenho dificuldade em apresentar situações em que gostaria de usar isso.
CodesInChaos
72
@CodeInChaos: Como eu disse, o ponto principal disso é se você deseja usá-lo no mesmo tipo de sistema, com o mesmo conjunto de funções. Caso contrário, você não deve usá-lo.
User541686
193
-1 Garanto que alguém (que não entende bytes x caracteres) deseje converter sua string em uma matriz de bytes, eles pesquisam no Google e lêem esta resposta e fazem a coisa errada, porque em quase todos casos, a codificação é relevante.
Artbristol
401
@artbristol: Se eles não podem se incomodar em ler a resposta (ou as outras respostas ...), desculpe-me, então não há melhor maneira de me comunicar com eles. Geralmente, opto por responder ao OP em vez de tentar adivinhar o que os outros podem fazer com a minha resposta - o OP tem o direito de saber, e só porque alguém pode abusar de uma faca não significa que precisamos esconder todas as facas do mundo para nós. Embora se você discordar, tudo bem também.
user541686
185
Esta resposta está errada em muitos níveis, mas principalmente por causa de sua declinação "você não precisa se preocupar com a codificação!". Os 2 métodos, GetBytes e GetString, são supérfluos na medida em que são apenas reimplementações do que Encoding.Unicode.GetBytes () e Encoding.Unicode.GetString () já fazem. A declaração "Contanto que seu programa (ou outros programas) não tente interpretar os bytes" também é fundamentalmente falha, pois implicitamente eles significam que os bytes devem ser interpretados como Unicode.
David
1108

Depende da codificação da sua string ( ASCII , UTF-8 , ...).

Por exemplo:

byte[] b1 = System.Text.Encoding.UTF8.GetBytes (myString);
byte[] b2 = System.Text.Encoding.ASCII.GetBytes (myString);

Uma pequena amostra da importância da codificação:

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

O ASCII simplesmente não está equipado para lidar com caracteres especiais.

Internamente, a estrutura .NET usa UTF-16 para representar seqüências de caracteres; portanto, se você deseja obter os bytes exatos que o .NET usa, use System.Text.Encoding.Unicode.GetBytes (...).

Consulte Codificação de caracteres no .NET Framework (MSDN) para obter mais informações.

bmotmans
fonte
14
Mas, por que a codificação deve ser levada em consideração? Por que simplesmente não consigo obter os bytes sem precisar ver qual codificação está sendo usada? Mesmo se necessário, o próprio objeto String não deve saber qual codificação está sendo usada e simplesmente despejar o que está na memória?
Agnel Kurian
57
As seqüências de caracteres .NET são sempre codificadas como Unicode. Então use System.Text.Encoding.Unicode.GetBytes (); para obter o conjunto de bytes que o .NET usaria para representar os caracteres. No entanto, por que você quer isso? Eu recomendo UTF-8, especialmente quando a maioria dos caracteres está no conjunto latino ocidental.
21139 AnthonyWJones
8
Além disso: os bytes exatos usados ​​internamente na cadeia de caracteres não importam se o sistema que os recupera não manipula essa codificação ou a codifica incorreta. Se tudo estiver dentro do .Net, por que converter para uma matriz de bytes? Caso contrário, é melhor ser explícito com sua codificação
Joel Coehoorn
11
@ Joel, tenha cuidado com System.Text.Encoding.Default, pois pode ser diferente em cada máquina em que é executado. É por isso que é recomendável sempre especificar uma codificação, como UTF-8.
Ash
25
Você não precisa das codificações, a menos que você (ou outra pessoa) realmente pretenda interpretar os dados, em vez de tratá-los como um "bloco de bytes" genérico. Para coisas como compactação, criptografia, etc., preocupar-se com a codificação não faz sentido. Veja minha resposta para uma maneira de fazer isso sem se preocupar com a codificação. (Eu poderia ter dado um -1 para dizer que você precisa se preocupar com codificações quando você não faz, mas eu não estou me sentindo particularmente média hoje: P.)
user541686
285

A resposta aceita é muito, muito complicada. Use as classes .NET incluídas para isso:

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

Não reinvente a roda se não precisar ...

Erik A. Brandstadmoen
fonte
14
Caso a resposta aceita seja alterada, para fins de registro, é a resposta de Mehrdad nesta hora e data atuais. Esperamos que o OP revisite isso e aceite uma solução melhor.
Thomas Eding 27/09/13
7
bom em princípio, mas a codificação deve System.Text.Encoding.Unicodeser equivalente à resposta de Mehrdad.
precisa
5
A pergunta foi editada um milhão de vezes desde a resposta original, portanto, talvez minha resposta esteja um pouco desatualizada. Eu nunca pretendi dar um exace equivalente à resposta de Mehrdad, mas dar uma maneira sensata de fazê-lo. Mas, você pode estar certo. No entanto, a frase "obter em que bytes a cadeia foi armazenada" na pergunta original é muito imprecisa. Armazenado, onde? Em memória? No disco? Se estiver na memória, System.Text.Encoding.Unicode.GetBytesprovavelmente seria mais preciso.
Erik A. Brandstadmoen
7
@ AMissico, sua sugestão é incorreta, a menos que você tenha certeza de que sua string é compatível com a codificação padrão do sistema (string contendo apenas caracteres ASCII no conjunto de caracteres herdado padrão do sistema). Mas em nenhum lugar o OP afirma isso.
Frédéric
5
@ AMissico No entanto, ele pode causar resultados diferentes em diferentes sistemas . Isso nunca é uma coisa boa. Mesmo que seja para fazer um hash ou algo assim (presumo que seja o que OP significa com 'criptografar'), a mesma string ainda deve sempre fornecer o mesmo 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
fonte
2
Você pode usar a mesma instância BinaryFormatter para todas essas operações
Joel Coehoorn
3
Muito interessante. Aparentemente, ele eliminará qualquer caractere Unicode substituto alto. Consulte a documentação em [BinaryFormatter ]
95

Você precisa levar em consideração a codificação, porque 1 caractere pode ser representado por 1 ou mais bytes (até cerca de 6) e codificações diferentes tratam esses bytes de maneira diferente.

Joel tem uma postagem sobre isso:

O mínimo absoluto que todo desenvolvedor de software deve saber absolutamente, positivamente sobre Unicode e conjuntos de caracteres (sem desculpas!)

Zhaph - Ben Duguid
fonte
6
"1 caractere pode ser representado por 1 ou mais bytes", eu concordo. Eu só quero esses bytes, independentemente da codificação da string. A única maneira de armazenar uma string na memória é em bytes. Caracteres pares são armazenados como 1 ou mais bytes. Eu apenas quero colocar minhas mãos neles bytes.
Agnel Kurian
16
Você não precisa das codificações, a menos que você (ou outra pessoa) realmente pretenda interpretar os dados, em vez de tratá-los como um "bloco de bytes" genérico. Para coisas como compactação, criptografia, etc., preocupar-se com a codificação não faz sentido. Veja minha resposta para uma maneira de fazer isso sem se preocupar com a codificação.
user541686
9
@Mehrdad - Totalmente, mas a pergunta original, como afirmei quando respondi inicialmente, não fez ressalvas sobre o OP que aconteceria com esses bytes depois que eles foram convertidos e, para futuros pesquisadores, as informações pertinentes são pertinentes. coberto pela resposta de Joel bastante bem - e como você declara na sua resposta: desde que você se mantenha no mundo .NET e use seus métodos para converter de / para, você está feliz. Assim que você sair disso, a codificação será importante.
Zhaph - Ben Duguid
Um ponto de código pode ser representado por até 4 bytes. (Uma unidade de código UTF-32, um par substituto UTF-16 ou 4 bytes de UTF-8.) Os valores pelos quais o UTF-8 precisaria de mais de 4 bytes estão fora do intervalo 0x0..0x10FFFF do Unicode. ;-)
DevSolar
89

Esta é uma pergunta popular. É importante entender o que o autor da pergunta está fazendo e que é diferente do que é provavelmente a necessidade mais comum. Para desencorajar o uso indevido do código onde ele não é necessário, respondi primeiro mais tarde.

Necessidade comum

Cada string possui um conjunto de caracteres e codificação. Quando você converte um System.Stringobjeto em uma matriz, System.Bytevocê ainda tem um conjunto de caracteres e codificação. Para a maioria dos usos, você saberia qual conjunto de caracteres e codificação precisa e o .NET simplifica a "cópia com conversão". Basta escolher a Encodingclasse apropriada .

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

A conversão pode precisar lidar com casos em que o conjunto de caracteres de destino ou a codificação não suporta um caractere que está na origem. Você tem algumas opções: exceção, substituição ou pular. A política padrão é substituir um '?'.

// using System.Text;
var text = Encoding.ASCII.GetString(Encoding.ASCII.GetBytes("You win €100")); 
                                                      // -> "You win ?100"

Claramente, as conversões não são necessariamente sem perdas!

Nota: Para System.Stringo conjunto de caracteres de origem é Unicode.

A única coisa confusa é que o .NET usa o nome de um conjunto de caracteres para o nome de uma codificação específica desse conjunto de caracteres. Encoding.Unicodedeve ser chamado Encoding.UTF16.

É isso para a maioria dos usos. Se é isso que você precisa, pare de ler aqui. Veja o divertido artigo de Joel Spolsky se você não entender o que é uma codificação.

Necessidade específica

Agora, o autor da pergunta pergunta: "Toda string é armazenada como uma matriz de bytes, certo? Por que não posso simplesmente ter esses bytes?"

Ele não quer nenhuma conversão.

Na especificação do C # :

O processamento de caracteres e cadeias de caracteres em C # usa codificação Unicode. O tipo de caractere representa uma unidade de código UTF-16 e o ​​tipo de sequência representa uma sequência de unidades de código UTF-16.

Portanto, sabemos que se solicitarmos a conversão nula (ou seja, de UTF-16 para UTF-16), obteremos o resultado desejado:

Encoding.Unicode.GetBytes(".NET String to byte array")

Mas, para evitar a menção de codificações, devemos fazê-lo de outra maneira. Se um tipo de dados intermediário for aceitável, existe um atalho conceitual para isso:

".NET String to byte array".ToCharArray()

Isso não nos dá o tipo de dados desejado, mas a resposta de Mehrdad mostra como converter esse array Char em um array Byte usando o BlockCopy . No entanto, isso copia a string duas vezes! E também usa explicitamente código específico da codificação: o tipo de dados System.Char.

A única maneira de obter os bytes reais em que a String está armazenada é usar um ponteiro. A fixeddeclaração permite pegar o endereço dos valores. Na especificação do C #:

[Para] uma expressão do tipo string, ... o inicializador calcula o endereço do primeiro caractere na string.

Para fazer isso, o compilador grava o código pular as outras partes do objeto string com RuntimeHelpers.OffsetToStringData. Portanto, para obter os bytes brutos, basta criar um ponteiro para a string e copiar o número de bytes necessários.

// 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 o @CodesInChaos apontou, o resultado depende da resistência da máquina. Mas o autor da pergunta não está preocupado com isso.

Tom Blodget
fonte
3
@ Jan Isso está correto, mas o comprimento da string já fornece o número de unidades de código (não pontos de código).
Tom Blodget
1
Obrigado por apontar isso! No MSDN: "A Lengthpropriedade [of String] retorna o número de Charobjetos nessa instância, não o número de caracteres Unicode." Seu código de exemplo está, portanto, correto conforme escrito.
Jan Hettich
1
@supercat "O tipo char representa uma unidade de código UTF-16 e o ​​tipo de string representa uma sequência de unidades de código UTF-16." —_ Especificação C # 5._ Embora, sim, não exista nada que impeça uma string Unicode inválida:new String(new []{'\uD800', '\u0030'})
precisa
1
@TomBlodget: Curiosamente, se alguém pegar instâncias Globalization.SortKey, extrair KeyDatae compactar os bytes resultantes de cada um em um String[dois bytes por caractere, primeiro o MSB ], chamar String.CompareOrdinalas strings resultantes será substancialmente mais rápido do que SortKey.Compareas instâncias de SortKey, ou mesmo chamando memcmpessas instâncias. Dado isso, eu me pergunto por que KeyDataretorna um Byte[]e não um String?
Supercat
1
Infelizmente, a resposta certa, mas anos tarde demais, nunca terá tantos votos quanto os aceitos. Devido ao TL, as pessoas do DR acham que a resposta aceita é ótima. copyenpastit e up-vote.
Martin Capodici
46

A primeira parte da sua pergunta (como obter os bytes) já foi respondida por outras pessoas: procure no System.Text.Encodingespaço para nome.

Abordarei sua pergunta de acompanhamento: por que você precisa escolher uma codificação? Por que você não consegue isso da própria classe de strings?

A resposta está em duas partes.

Antes de tudo, os bytes usados ​​internamente pela classe de string não importam e, sempre que você assume, provavelmente está introduzindo um bug.

Se o seu programa estiver inteiramente dentro do mundo .Net, você não precisará se preocupar em obter matrizes de bytes para seqüências de caracteres, mesmo se estiver enviando dados pela rede. Em vez disso, use .Net Serialization para se preocupar em transmitir os dados. Você não se preocupa mais com os bytes reais: o formatador de serialização faz isso por você.

Por outro lado, e se você estiver enviando esses bytes para algum lugar que você não pode garantir, extrairá dados de um fluxo serializado .Net? Nesse caso, você definitivamente precisa se preocupar com a codificação, porque obviamente esse sistema externo se importa. Então, novamente, os bytes internos usados ​​pela string não importam: você precisa escolher uma codificação para poder ser explícito sobre essa codificação no terminal receptor, mesmo que seja a mesma codificação usada internamente pelo .Net.

Entendo que, nesse caso, você pode preferir usar os bytes reais armazenados pela variável de cadeia de caracteres na memória sempre que possível, com a ideia de que isso pode poupar algum trabalho na criação do fluxo de bytes. No entanto, eu digo a você que isso não é importante em comparação com garantir que sua saída seja entendida na outra extremidade e garantir que você deve ser explícito com sua codificação. Além disso, se você realmente deseja corresponder aos bytes internos, já pode escolher a Unicodecodificação e obter essa economia de desempenho.

O que me leva à segunda parte ... escolher o Unicode codificação está dizendo ao .Net para usar os bytes subjacentes. Você precisa escolher essa codificação, porque quando sai um novo Unicode-Plus com novos fanpages, o tempo de execução do .Net precisa ser livre para usar esse modelo de codificação melhor e mais novo sem interromper o programa. Mas, no momento (e futuro previsível), basta escolher a codificação Unicode para obter o que você deseja.

Também é importante entender que sua string precisa ser reescrita para ser conectada, e isso envolve pelo menos alguma tradução do padrão de bits, mesmo quando você usa uma codificação correspondente . O computador precisa levar em conta coisas como Big vs Little Endian, ordem de bytes da rede, pacotes, informações da sessão etc.

Joel Coehoorn
fonte
9
Existem áreas no .NET em que você precisa obter matrizes de bytes para seqüências de caracteres. Muitas das classes de criptografia .NET contêm métodos como ComputeHash () que aceitam fluxo ou matriz de bytes. Você não tem alternativa, mas primeiro converter uma string em uma matriz de bytes (escolhendo uma codificação) e, em seguida, agrupá-la opcionalmente em um fluxo. No entanto, desde que você escolha uma codificação (por exemplo, UTF8), não haverá problemas com isso.
Ash
44

Apenas para demonstrar que o som do Mehrdrad resposta obras, sua abordagem pode até persistir os caracteres substitutos desemparelhados (dos quais muitos tinham levantadas contra minha resposta, mas da qual todos são igualmente culpados de, por exemplo System.Text.Encoding.UTF8.GetBytes, System.Text.Encoding.Unicode.GetBytes; esses métodos de codificação não pode persistir o substituto alto caracteres, d800por exemplo, e aqueles apenas substituem apenas caracteres substitutos altos por 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);
    }        
}

Resultado:

T 54
e 65
s 73
t 74
? d800
T 54
e 65
s 73
t 74

Tente isso com System.Text.Encoding.UTF8.GetBytes ou System.Text.Encoding.Unicode.GetBytes , eles apenas substituirão caracteres substitutos altos por valor fffd

Toda vez que há um movimento nesta pergunta, ainda estou pensando em um serializador (seja da Microsoft ou de um componente de terceiros) que possa persistir seqüências de caracteres, mesmo que contenha caracteres substitutos não emparelhados; Eu google isso de vez em quando: personagem substituto não emparelhado serialização .NET . Isso não me faz perder o sono, mas é meio irritante quando, de vez em quando, alguém comenta minha resposta que é falha, mas as respostas são igualmente falhas quando se trata de personagens substitutos não emparelhados.

Porra, a Microsoft deveria ter usado apenas System.Buffer.BlockCopyna suaBinaryFormatter

谢谢!

Michael Buen
fonte
3
Os substitutos não precisam aparecer em pares para formar pontos de código válidos? Se for esse o caso, entendo por que os dados seriam mutilados.
dtanders
1
@dtanders Sim, esses são meus pensamentos também, eles têm que aparecer em pares, personagens substitutos não emparelhados só acontecem se você os colocar deliberadamente na corda e torná-los emparelhados. O que eu não sei é por que outros desenvolvedores continuam insistindo que devemos usar a abordagem com reconhecimento de codificação, pois eles consideram a abordagem de serialização ( minha resposta , que foi uma resposta aceita por mais de três anos) não mantém os pares caráter substituto intacto. Mas se esqueceram de verificar se as suas soluções de codificação-aware não manter o caráter substituto não pareado também, a ironiaツ
Michael Buen
Se houver uma biblioteca de serialização usada System.Buffer.BlockCopyinternamente, todos os argumentos do pessoal de defesa de codificação serão discutidos
Michael Buen
2
@ MichaelBuen Parece-me que a questão principal é que você está em grandes letras em negrito dizendo que algo não importa, em vez de dizer que não importa no caso deles. Como resultado, você está incentivando as pessoas que olham para a sua resposta a cometer erros básicos de programação que causarão frustração a outras pessoas no futuro. Os substitutos não emparelhados são inválidos em uma sequência. Como não é uma matriz de caracteres, faz sentido que a conversão de uma string para outro formato resulte em erro FFFDnesse caractere. Se você deseja manipular manualmente as cordas, use um char [] conforme recomendado.
Trisped
2
@ dtanders: A System.Stringé uma sequência imutável de Char; O .NET sempre permitiu que um Stringobjeto fosse construído a partir de qualquer um Char[]e exporte seu conteúdo para um Char[]contendo os mesmos valores, mesmo que o original Char[]contenha substitutos não emparelhados.
Supercat #
41

Tente isso, muito menos código:

System.Text.Encoding.UTF8.GetBytes("TEST String");
Nathan
fonte
Então tente isso System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép);e chore! Funcionará, mas System.Text.Encoding.UTF8.GetBytes("Árvíztűrő tükörfúrógép").Length != System.Text.Encoding.UTF8.GetBytes("Arvizturo tukorfurogep").Lengthenquanto"Árvíztűrő tükörfúrógép".Length == "Arvizturo tukorfurogep".Length
mg30rg 5/17/17
9
@ mg30rg: Por que você acha que seu exemplo é estranho? Certamente, em uma codificação de largura variável, nem todos os caracteres têm o mesmo comprimento de bytes. O que há de errado com isso?
Vlad
@Vlad Um comentário mais válido aqui, porém, é que, como símbolos unicode codificados (assim, como bytes), caracteres que incluem seus próprios sinais diacríticos fornecerão um resultado diferente do que os sinais diacríticos divididos em símbolos modificadores adicionados ao personagem. Mas iirc existem métodos no .net para separá-los especificamente, para permitir obter uma representação consistente de bytes.
Nyerguds 31/03
25

Bem, eu li todas as respostas e elas eram sobre o uso de codificação ou uma sobre serialização que descarta substitutos não emparelhados.

É ruim quando a string, por exemplo, vem do SQL Server, onde foi criada a partir de uma matriz de bytes que armazena, por exemplo, um hash de senha. Se retirarmos algo dele, ele armazenará um hash inválido e, se quisermos armazená-lo em XML, queremos deixá-lo intacto (porque o gravador de XML descarta uma exceção em qualquer substituto não emparelhado que encontrar).

Então, eu uso a codificação Base64 de matrizes de bytes nesses casos, mas, na Internet, existe apenas uma solução para isso em C #, e ela possui um bug e é apenas uma maneira, então eu corrigi o bug e escrevi de volta procedimento. Aqui estão, 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
fonte
Em vez de usar seu método personalizado para converter uma matriz de bytes em base64, tudo o que você precisava fazer era usar o conversor interno: Convert.ToBase64String (arr);
Makotosan
@ Makotosan obrigado, mas usei Convert.ToBase64String(arr); para as conversões base64 byte[] (data) <-> string (serialized data to store in XML file). Mas, para obter a inicial byte[] (data)que eu precisava fazer algo com um Stringque continha binários de dados (que é a maneira MSSQL devolveu para mim). Portanto, as funções acima são para String (binary data) <-> byte[] (easy accessible binary data).
Gman
23

Também explique por que a codificação deve ser levada em consideração. Não posso simplesmente obter em que bytes a cadeia foi armazenada? Por que essa dependência na codificação? !!!

Porque não existe algo como "os bytes da string".

Uma string (ou mais genericamente, um texto) é composta de caracteres: letras, dígitos e outros símbolos. Isso é tudo. Os computadores, no entanto, não sabem nada sobre caracteres; eles podem lidar apenas com bytes. Portanto, se você deseja armazenar ou transmitir texto usando um computador, é necessário transformar os caracteres em bytes. Como você faz isso? Aqui é onde as codificações entram em cena.

Uma codificação nada mais é do que uma convenção para converter caracteres lógicos em bytes físicos. A codificação mais simples e mais conhecida é ASCII, e é tudo o que você precisa se escrever em inglês. Para outros idiomas, você precisará de codificações mais completas, sendo que qualquer um dos Unicode oferece a opção mais segura atualmente.

Então, resumindo, tentar "obter os bytes de uma string sem usar codificações" é tão impossível quanto "escrever um texto sem usar nenhum idioma".

A propósito, eu recomendo fortemente que você (e qualquer pessoa, nesse caso) leia este pequeno pedaço de sabedoria: O mínimo absoluto que todo desenvolvedor de software deve absolutamente saber positivamente, positivamente sobre Unicode e conjuntos de caracteres (sem desculpas!)

Konamiman
fonte
2
Permitam-me esclarecer: Uma codificação foi usada para converter "olá mundo" em bytes físicos. Como a string é armazenada no meu computador, tenho certeza de que deve ser armazenada em bytes. Eu apenas quero acessar esses bytes para salvá-los em disco ou por qualquer outro motivo. Eu não quero interpretar esses bytes. Como não quero interpretar esses bytes, a necessidade de uma codificação nesse momento é tão equivocada quanto exigir uma linha telefônica para chamar printf.
Agnel Kurian
3
Mas, novamente, não há conceito de tradução de texto em bytes físicos, a menos que você use uma codificação. Certamente, o compilador armazena as seqüências de alguma forma na memória - mas está apenas usando uma codificação interna que você (ou qualquer pessoa, exceto o desenvolvedor do compilador) não conhece. Portanto, faça o que fizer, você precisará de uma codificação para obter bytes físicos de uma string.
22909 Konamiman
@Agnel Kurian: É claro que uma string tem um monte de bytes em algum lugar que armazena seu conteúdo (UTF-16 de graça). Mas há um bom motivo para impedir que você o acesse: as strings são imutáveis ​​e se você pudesse obter a matriz de bytes [] interna, também poderia modificá-la. Isso quebra a imutabilidade, o que é vital porque várias seqüências de caracteres podem compartilhar os mesmos dados. Usar uma codificação UTF-16 para obter a sequência provavelmente copiará os dados.
Ollb 14/05
2
@Gnafoo, uma cópia dos bytes serve.
Agnel Kurian
22

C # para converter um stringem uma bytematriz:

public static byte[] StrToByteArray(string str)
{
   System.Text.UTF8Encoding  encoding=new System.Text.UTF8Encoding();
   return encoding.GetBytes(str);
}
Shyam sundar shah
fonte
17
byte[] strToByteArray(string str)
{
    System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
    return enc.GetBytes(str);
}
gkrogers
fonte
Mas, por que a codificação deve ser levada em consideração? Por que simplesmente não consigo obter os bytes sem precisar ver qual codificação está sendo usada? Mesmo se necessário, o próprio objeto String não deve saber qual codificação está sendo usada e simplesmente despejar o que está na memória?
Agnel Kurian
5
Isso nem sempre funciona. Alguns caracteres especiais podem se perder usando esse método que encontrei da maneira mais difícil.
JB rei
17

Você pode usar o seguinte código para conversão entre string e 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
fonte
VUPthis um problema resolvido minha (byte [] ff = ASCIIEncoding.ASCII.GetBytes (barcodetxt.Text);)
r.hamd
16

Com o advento do Span<T>C # 7.2, a técnica canônica para capturar a representação de memória subjacente de uma cadeia de caracteres em uma matriz de bytes gerenciados é:

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

A conversão para trás não deve ser iniciada, porque isso significa que você está realmente interpretando os dados de alguma forma, mas por uma questão de integridade:

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

Os nomes NonPortableCaste DangerousGetPinnableReferencedevem promover o argumento de que você provavelmente não deveria estar fazendo isso.

Observe que o trabalho Span<T>requer a instalação do pacote System.Memory NuGet .

Independentemente disso, a pergunta original real e os comentários de acompanhamento implicam que a memória subjacente não está sendo "interpretada" (o que eu suponho que o meio não seja modificado ou lido além da necessidade de escrevê-la como está), indicando que alguma implementação da Streamclasse deve ser usado em vez de raciocinar sobre os dados como cadeias de caracteres.

John Rasch
fonte
13

Não tenho certeza, mas acho que a string armazena suas informações como uma matriz de caracteres, que é ineficiente em bytes. Especificamente, a definição de um Char é "Representa um caractere Unicode".

pegue este exemplo de exemplo:

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

Observe que a resposta Unicode é de 14 bytes em ambas as instâncias, enquanto a resposta UTF-8 é de apenas 9 bytes para o primeiro e apenas 7 para o segundo.

Portanto, se você quiser apenas os bytes usados ​​pela string, basta usar Encoding.Unicode, mas será ineficiente com o espaço de armazenamento.

Ed Marty
fonte
10

O principal problema é que um glifo em uma string leva 32 bits (16 bits para um código de caractere), mas um byte tem apenas 8 bits de sobra. Um mapeamento individual não existe, a menos que você se restrinja a sequências que contêm apenas caracteres ASCII. System.Text.Encoding tem várias maneiras de mapear uma string para byte [], você precisa escolher uma que evite a perda de informações e que seja fácil de usar pelo seu cliente quando ela precisar mapear o byte [] de volta para uma string .

Utf8 é uma codificação popular, compacta e sem perdas.

Hans Passant
fonte
3
O UTF-8 é compacto apenas se a maioria dos seus caracteres estiver no conjunto de caracteres em inglês (ASCII). Se você tivesse uma longa sequência de caracteres chineses, o UTF-16 seria uma codificação mais compacta que o UTF-8 para essa sequência. Isso ocorre porque o UTF-8 usa um byte para codificar ASCII e 3 (ou talvez 4) caso contrário.
Joel Mueller
7
Verdade. Mas como você pode não saber sobre codificação se estiver familiarizado com o tratamento de texto em chinês?
Hans Passant
9

Usar:

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

O resultado é:

[0] = 115
[1] = 116
[2] = 114
[3] = 105
[4] = 110
[5] = 103
mashet
fonte
OP pede especificamente para não especificar uma codificação ... "sem especificar manualmente uma codificação específica"
Ferdz
8

Maneira mais rápida

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

EDITAR como Makotosan comentou que agora é a melhor maneira:

Encoding.UTF8.GetBytes(text)
Alessandro Annini
fonte
8
ASCIIEncoding ..... não é necessário. Simplesmente usando Encoding.UTF8.GetBytes (texto) é o preferido.
21712 Makotosan
8

Como converter uma seqüência de caracteres em um byte [] no .NET (C #) sem especificar manualmente uma codificação específica?

Uma string no .NET representa o texto como uma sequência de unidades de código UTF-16, portanto, os bytes já estão codificados na memória em UTF-16.

Resposta de Mehrdad

Você pode usar a resposta de Mehrdad , mas na verdade usa uma codificação porque os caracteres são UTF-16. Ele chama ToCharArray que, olhando a fonte, cria uma char[]e copia a memória diretamente para ela. Em seguida, ele copia os dados para uma matriz de bytes que também é alocada. Portanto, ele está copiando os bytes subjacentes duas vezes e alocando uma matriz de caracteres que não é usada após a chamada.

Resposta de Tom Blodget

A resposta de Tom Blodget é 20 a 30% mais rápida que a Mehrdad, uma vez que pula a etapa intermediária de alocar um array de caracteres e copiar os bytes para ele, mas requer a compilação da /unsafeopção. Se você absolutamente não deseja usar a codificação, acho que este é o caminho a seguir. Se você colocar seu login de criptografia dentro do fixedbloco, nem precisará alocar uma matriz de bytes separada e copiar os bytes para ela.

Além disso, por que a codificação deve ser levada em consideração? Não posso simplesmente obter em que bytes a cadeia foi armazenada? Por que existe uma dependência nas codificações de caracteres?

Porque essa é a maneira correta de fazer isso. stringé uma abstração.

Usar uma codificação pode causar problemas se você tiver 'strings' com caracteres inválidos, mas isso não deve acontecer. Se você está recebendo dados em sua string com caracteres inválidos, está fazendo errado. Você provavelmente deveria estar usando uma matriz de bytes ou uma codificação Base64 para começar.

Se você usar System.Text.Encoding.Unicode, seu código será mais resistente. Você não precisa se preocupar com a continuidade do sistema em que seu código estará sendo executado. Você não precisa se preocupar se a próxima versão do CLR usará uma codificação de caracteres internos diferente.

Acho que a pergunta não é por que você quer se preocupar com a codificação, mas por que deseja ignorá-la e usar outra coisa. Codificação pretende representar a abstração de uma sequência em uma sequência de bytes. System.Text.Encoding.Unicodefornecerá uma pequena codificação de ordem de bytes endian e executará o mesmo em todos os sistemas, agora e no futuro.

Jason Goemaat
fonte
Na verdade, uma string em C # NÃO é restrita a apenas UTF-16. O que é verdade é que ele contém um vetor de unidades de código de 16 bits, mas essas unidades de código de 16 bits não estão restritas ao UTF-16 válido. Mas como são de 16 bits, você precisa de uma codificação (ordem dos bytes) para convertê-los em 8 bits. Uma string pode então armazenar dados não Unicode, incluindo código binário (por exemplo, uma imagem de bitmap). Torna-se interpretado como UTF-16 apenas nos formatadores de E / S e texto que fazem essa interpretação.
verdy_p
Portanto, em uma string C #, você pode armazenar com segurança uma unidade de código como 0xFFFF ou 0xFFFE, mesmo que não sejam caracteres em UTF-16, e você pode armazenar um 0xD800 isolado que não seja seguido por uma unidade de código em 0xDC00..0xDFFF (por exemplo, substitutos não emparelhados inválidos em UTF-16). A mesma observação se aplica a cadeias de caracteres em Javascript / ECMAscript e Java.
verdy_p
Quando você usa "GetBytes", é claro que não especifica uma codificação, mas assume uma ordem de bytes para obter os dois bytes em uma especificação para cada unidade de código armazenada localmente na string. Ao criar uma nova sequência de bytes, você também precisa de um conversor, não necessariamente UTF-8 para UTF-16, pode inserir o 0 extra no byte alto ou compactar dois bytes (no MSB primeiro ou LSB primeiro) em a mesma unidade de código de 16 bits. Strings são então um formato compacto para matrizes de números inteiros de 16 bits. A relação com "personagens" é outro problema, em C # são tipos não reais, eles ainda são representados como strings
verdy_p
7

A abordagem mais próxima da pergunta do OP é a de Tom Blodget, que realmente entra no objeto e extrai os bytes. Eu digo o mais próximo porque depende da implementação do String Object.

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

Claro, mas é aí que surge o erro fundamental na pergunta. A String é um objeto que pode ter uma estrutura de dados interessante. Já sabemos que sim, porque permite que os substitutos não pareados sejam armazenados. Pode armazenar o comprimento. Pode manter um ponteiro para cada um dos substitutos 'emparelhados', permitindo uma contagem rápida. Etc. Todos esses bytes extras não fazem parte dos dados dos caracteres.

O que você deseja são os bytes de cada caractere em uma matriz. E é aí que entra a 'codificação'. Por padrão, você obterá o UTF-16LE. Se você não se importa com os bytes, exceto com a ida e volta, pode escolher qualquer codificação, incluindo o 'padrão', e convertê-lo mais tarde (assumindo os mesmos parâmetros, como qual era a codificação padrão, pontos de código, correções de bugs) , coisas permitidas, como substitutos não pareados, etc.

Mas por que deixar a 'codificação' mágica? Por que não especificar a codificação para saber quais bytes você receberá?

"Why is there a dependency on character encodings?"

Codificação (neste contexto) significa simplesmente os bytes que representam sua string. Não os bytes do objeto string. Você queria os bytes em que a string foi armazenada - é aqui que a pergunta foi feita ingenuamente. Você queria os bytes da string em uma matriz contígua que representa a string, e não todos os outros dados binários que um objeto string pode conter.

O que significa que uma string é armazenada é irrelevante. Você deseja uma string "Codificada" em bytes em uma matriz de bytes.

Gosto da resposta de Tom Bloget porque ele levou você na direção dos 'bytes do objeto de string'. É dependente da implementação e, como ele está espiando os internos, pode ser difícil reconstituir uma cópia da string.

A resposta de Mehrdad está errada porque é enganosa no nível conceitual. Você ainda tem uma lista de bytes codificados. Sua solução específica permite a preservação de substitutos não emparelhados - isso depende da implementação. Sua solução em particular não produziria os bytes da string com precisão se GetBytesretornasse a string em UTF-8 por padrão.


Eu mudei de idéia sobre isso (solução de Mehrdad) - isso não está recebendo os bytes da string; ao contrário, está obtendo os bytes da matriz de caracteres que foi criada a partir da string. Independentemente da codificação, o tipo de dados char em c # é um tamanho fixo. Isso permite que uma matriz de bytes de comprimento consistente seja produzida e permite que a matriz de caracteres seja reproduzida com base no tamanho da matriz de bytes. Portanto, se a codificação fosse UTF-8, mas cada caractere tivesse 6 bytes para acomodar o maior valor utf8, ainda funcionaria. Então, de fato - a codificação do personagem não importa.

Mas uma conversão foi usada - cada caractere foi colocado em uma caixa de tamanho fixo (tipo de caractere do c #). No entanto, o que é essa representação não importa, o que é tecnicamente a resposta para o PO. Então - se você vai converter de qualquer maneira ... Por que não 'codificar'?

Gerard ONeill
fonte
Esses caracteres não são suportados por UTF-8 ou UTF-16 ou mesmo UTF-32 por exemplo: 񩱠& (Char) 55906& (Char) 55655. Portanto, você pode estar errado e a resposta de Mehrdad é uma conversão segura sem considerar que tipo de codificação é usada.
Mojtaba Rezaeian
Raymon, os caracteres já estão representados por algum valor unicode - e todos os valores unicode podem ser representados por todos os utfs. Existe uma explicação mais longa sobre o que você está falando? Em qual codificação de caracteres esses dois valores (ou 3 ..) existem?
precisa
São caracteres inválidos que não são suportados por nenhum intervalo de codificação. Isso não significa que eles são 100% inúteis. Um código que converte qualquer tipo de string em seu equivalente de matriz de bytes, independentemente das codificações, não é uma solução errada e tem seu próprio uso nas ocasiões desejadas.
Mojtaba Rezaeian
1
Ok, então eu acho que você não está entendendo o problema. Sabemos que é uma matriz compatível com unicode - na verdade, por ser .net, sabemos que é UTF-16. Portanto, esses caracteres não existirão lá. Você também não leu completamente meu comentário sobre as mudanças nas representações internas. Uma String é um objeto, não uma matriz de bytes codificada. Então, eu vou discordar da sua última declaração. Você deseja que o código converta todas as seqüências unicode em qualquer codificação UTF. Isso faz o que você deseja, corretamente.
precisa
Objetos são sequência de dados originalmente sequência de bits que descreve um objeto em seu estado atual. Portanto, todos os dados nas linguagens de programação são conversíveis em matriz de bytes (cada byte define 8 bits), pois pode ser necessário manter algum estado de qualquer objeto na memória. Você pode salvar e manter uma sequência de bytes no arquivo ou na memória e convertê-la como número inteiro, bigint, imagem, sequência Ascii, sequência UTF-8, sequência criptografada ou seu próprio tipo de dados definido após a leitura do disco. Portanto, você não pode dizer que os objetos são algo diferente da sequência de bytes.
Mojtaba Rezaeian
6

Você pode usar o seguinte código para converter um stringpara um byte arrayno .NET

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

Se você realmente deseja uma cópia dos bytes subjacentes de uma sequência, pode usar uma função como a seguinte. No entanto, você não deve ler para descobrir o porquê.

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

Essa função fornece uma cópia dos bytes subjacentes à sua string, rapidamente. Você receberá esses bytes da maneira que eles estiverem codificando no seu sistema. Essa codificação é quase certamente UTF-16LE, mas é um detalhe de implementação que você não deveria se preocupar.

Seria mais seguro, mais simples e mais confiável apenas ligar,

System.Text.Encoding.Unicode.GetBytes()

Provavelmente, isso dará o mesmo resultado, é mais fácil de digitar e os bytes sempre farão ida e volta com uma chamada para

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

Aqui está minha implementação insegura de Stringpara Byte[]conversão:

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

É muito mais rápido que o da resposta aceita, mesmo que não seja tão elegante quanto é. Aqui estão meus benchmarks de cronômetro com mais de 10000000 iterações:

[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 usá-lo, você deve marcar "Permitir Código Não Seguro" nas propriedades de construção do seu projeto. Conforme o .NET Framework 3.5, esse método também pode ser usado como extensão String:

public static unsafe class StringExtensions
{
    public static Byte[] ToByteArray(this String s)
    {
        // Method Code
    }
}
Tommaso Belluzzo
fonte
O valor de RuntimeHelpers.OffsetToStringDataum múltiplo de 8 nas versões do Itanium do .NET? Porque, caso contrário, isso falhará devido às leituras desalinhadas.
Jon Hanna
não seria mais simples invocar memcpy? Você está em
Página Inicial>
2

Simplesmente use isto:

byte[] myByte= System.Text.ASCIIEncoding.Default.GetBytes(myString);
alireza amini
fonte
2
... e perca todos os caracteres com um salto superior a 127. Na minha língua nativa, é perfeitamente válido escrever "iniciaríztűrő tükörfúrógép". System.Text.ASCIIEncoding.Default.GetBytes("Árvíztűrő tükörfúrógép.").ToString();retornará "Árvizturo tukörfurogép."informações perdedoras que não podem ser recuperadas. (E eu ainda não mencionar idiomas asiáticos onde você perderia todos os caracteres.)
mg30rg
2

A cadeia de caracteres pode ser convertida em matriz de bytes de algumas maneiras diferentes, devido ao seguinte fato: O .NET suporta Unicode e o Unicode padroniza várias codificações de diferença chamadas UTFs. Eles têm comprimentos diferentes de representação de bytes, mas são equivalentes no sentido em que, quando uma string é codificada, ela pode ser codificada de volta para a string, mas se a string for codificada com um UTF e decodificada na suposição de UTF diferente, se puder ser parafusada acima.

Além disso, o .NET suporta codificações não-Unicode, mas elas não são válidas em casos gerais (serão válidas apenas se um subconjunto limitado de ponto de código Unicode for usado em uma sequência real, como ASCII). Internamente, o .NET suporta UTF-16, mas para a representação de fluxo, o UTF-8 geralmente é usado. É também um padrão de fato para a Internet.

Não surpreendentemente, a serialização de string em uma matriz de bytes e desserialização é suportada pela classe System.Text.Encoding, que é uma classe abstrata; suas classes derivadas suportam codificações concretas: ASCIIEncodinge quatro UTFs ( System.Text.UnicodeEncodingsuporta UTF-16)

Ref este link.

Para serialização em uma matriz de bytes usando System.Text.Encoding.GetBytes. Para a operação inversa, use System.Text.Encoding.GetChars. Essa função retorna uma matriz de caracteres; portanto, para obter uma string, use um construtor de string System.String(char[]).
Ref nesta página.

Exemplo:

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

Depende do que você deseja que os bytes FOR

Isso ocorre porque, como Tyler disse tão apropriadamente , "Strings não são dados puros. Eles também têm informações ". Nesse caso, as informações são uma codificação que foi assumida quando a sequência foi criada.

Supondo que você tenha dados binários (em vez de texto) armazenados em uma sequência

Isso se baseia no comentário do OP em sua própria pergunta e é a pergunta correta se eu entender as dicas do OP no caso de uso.

Armazenar dados binários em seqüências de caracteres é provavelmente a abordagem errada, devido à codificação assumida mencionada acima! Qualquer programa ou biblioteca que armazene esses dados binários em um string(em vez de um byte[]array que seria mais apropriado) já perdeu a batalha antes de começar. Se eles estiverem enviando os bytes para você em uma solicitação / resposta REST ou qualquer coisa que deva transmitir cadeias, Base64 seria a abordagem correta.

Se você tiver uma sequência de texto com uma codificação desconhecida

Todos os outros responderam a esta pergunta incorreta incorretamente.

Se a string parecer boa, selecione uma codificação (de preferência uma começando com UTF), use a System.Text.Encoding.???.GetBytes()função correspondente e diga a quem você atribuir os bytes para a codificação selecionada.

NH.
fonte
2

Ao ser perguntado o que você pretende fazer com os bytes, você respondeu :

Eu vou criptografá-lo. Posso criptografá-lo sem converter, mas eu ainda gostaria de saber por que a codificação chega aqui. Apenas me dê os bytes é o que eu digo.

Independentemente de você pretender enviar esses dados criptografados pela rede, carregá-los novamente na memória posteriormente ou enviá-los para outro processo, você claramente pretende descriptografá- los em algum momento. Nesse caso, a resposta é que você está definindo um protocolo de comunicação. Um protocolo de comunicação não deve ser definido em termos de detalhes de implementação da sua linguagem de programação e do tempo de execução associado. Há várias razões para isso:

  • Pode ser necessário se comunicar com um processo implementado em um idioma ou tempo de execução diferente. (Isso pode incluir um servidor executando em outra máquina ou enviando a sequência para um cliente de navegador JavaScript, por exemplo.)
  • O programa pode ser reimplementado em um idioma ou tempo de execução diferente no futuro.
  • A implementação do .NET pode alterar a representação interna de seqüências de caracteres. Você pode achar que isso soa exagerado, mas isso realmente aconteceu no Java 9 para reduzir o uso de memória. Não há razão para o .NET não seguir o exemplo. Skeet sugere que o UTF-16 provavelmente não é o ideal hoje, dando origem ao emoji e outros blocos de Unicode que precisam de mais de 2 bytes para representar também, aumentando a probabilidade de que a representação interna possa mudar no futuro.

Para se comunicar (com um processo completamente diferente ou com o mesmo programa no futuro), você precisa definir seu protocolo estritamente para minimizar a dificuldade de trabalhar com ele ou criar bugs acidentalmente. Dependendo da representação interna do .NET, não é uma definição estrita, clara ou até garantida como consistente. Uma codificação padrão é uma definição estrita que não falhará no futuro.

Em outras palavras, você não pode satisfazer seus requisitos de consistência sem especificar uma codificação.

Você certamente pode optar por usar o UTF-16 diretamente se achar que seu processo tem um desempenho significativamente melhor, já que o .NET o usa internamente ou por qualquer outro motivo, mas você precisa escolher essa codificação explicitamente e executar essas conversões explicitamente no seu código, em vez de depender na implementação interna do .NET.

Então escolha uma codificação e use-a:

using System.Text;

// ...

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

Como você pode ver, também é realmente menos código apenas usar os objetos de codificação incorporados do que implementar seus próprios métodos de leitura / gravação.

jpmc26
fonte
1

Dois caminhos:

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

E,

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

Eu costumo usar a parte inferior com mais frequência do que a parte superior, não os avaliei quanto à velocidade.


fonte
4
E os caracteres multibyte?
Agnel Kurian
c.ToByte () é privado: S
Khodor
@AgnelKurian Msdn diz "Este método retorna um valor de byte não assinado que representa o código numérico do objeto Char passado para ele. No .NET Framework, um objeto Char é um valor de 16 bits. Isso significa que o método é adequado para retornar os códigos numéricos de caracteres no intervalo de caracteres ASCII ou nos intervalos Unicode C0 Controls e Basic Latin e C1 Controls e Latin-1 Supplement, 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
user1120193
fonte