Como posso transformar uma string em UTF-8 em C #?

146

Tenho uma sequência que recebo de um aplicativo de terceiros e gostaria de exibi-la corretamente em qualquer idioma usando C # no Windows Surface.

Devido à codificação incorreta, um pedaço da minha string fica assim em espanhol:

Acción

considerando que deve ficar assim:

Acción

De acordo com a resposta desta pergunta: Como saber a codificação de string em C # , a codificação que estou recebendo já deve estar no UTF-8, mas é lida em Encoding.Default (provavelmente ANSI?).

Estou tentando transformar essa string em UTF-8 real, mas um dos problemas é que só consigo ver um subconjunto da classe Encoding (somente propriedades UTF8 e Unicode), provavelmente porque estou limitado à API de superfície do Windows.

Eu tentei alguns trechos que encontrei na internet, mas nenhum deles foi bem-sucedido até agora para os idiomas do leste (ou seja, coreano). Um exemplo é o seguinte:

var utf8 = Encoding.UTF8;
byte[] utfBytes = utf8.GetBytes(myString);
myString= utf8.GetString(utfBytes, 0, utfBytes.Length);     

Eu também tentei extrair a string em uma matriz de bytes e, em seguida, usar UTF8.GetString:

byte[] myByteArray = new byte[myString.Length];
for (int ix = 0; ix < myString.Length; ++ix)
{
    char ch = myString[ix];
    myByteArray[ix] = (byte) ch;
}

myString = Encoding.UTF8.GetString(myByteArray, 0, myString.Length);

Vocês têm outras idéias que eu poderia tentar?

Gaara
fonte
5
Seu problema está vindo do código que criou a string (de um fluxo ou byte []) em primeiro lugar. Por favor, mostre esse código.
SLaks
1
@Oded: .Net strings são armazenadas na memória como UTF16, mas Encoding.Defaultretornam a página de código ANSI do sistema.
SLaks
Aqui está um exemplo de uma string que não funciona no idioma inglês: em vez de exibir os dias, meu aplicativo front-end está exibindo: day's
Gaara

Respostas:

251

Como você sabe que a string está chegando, Encoding.Defaultvocê pode simplesmente usar:

byte[] bytes = Encoding.Default.GetBytes(myString);
myString = Encoding.UTF8.GetString(bytes);

Outra coisa que você deve lembrar: Se você estiver usando o Console.WriteLine para produzir algumas strings, também deverá escrever Console.OutputEncoding = System.Text.Encoding.UTF8;!!! Ou todas as strings utf8 serão exibidas como gbk ...

anothershrubery
fonte
Isso funciona também é realmente muito melhor do que a minha resposta, que também funciona eu estou dando-lhe uma obra +1 agradável
MethodMan
Obrigado! O problema é que, como mencionei na descrição, a API para superfície está incompleta (nenhum Encoding.Default disponível para mim).
Gaara
3
@ Gaara: Tente Encoding.GetEncoding(...); você precisará encontrar o nome da codificação real que foi usada incorretamente na outra extremidade.
SLaks
1
você pode explicar por que isso funciona? se o padrão for GB2312, o Encoding.Default.GetBytes codificará a cadeia de caracteres em bytes usando o codificador GB2312 e o Encoding.UTF8.GetString tentará decodificar a matriz de bytes usando o decodificador UTF8, o resultado deve estar errado, mas por que isso funciona. @anothershrubery
guorongfei
1
@guorongfei A premissa é que myStringé mojibake. O código primeiro desfaz a decodificação incorreta e depois a decodificação correta. Funciona desde que a decodificação incorreta não tenha perdido dados. Mas, como o @SLaks apontou, seria melhor usar a codificação exata que estava errada. (Melhor nomes e comentários no código ajudaria na compreensão de como código de muito errado com aparência é na verdade uma tentativa de fazer direito.)
Tom Blodget
17
string utf8String = "Acción";
string propEncodeString = string.Empty;

byte[] utf8_Bytes = new byte[utf8String.Length];
for (int i = 0; i < utf8String.Length; ++i)
{
   utf8_Bytes[i] = (byte)utf8String[i];
}

propEncodeString = Encoding.UTF8.GetString(utf8_Bytes, 0, utf8_Bytes.Length);

A saída deve se parecer com

Acción

dia exibe dia

chamar DecodeFromUtf8 ();

private static void DecodeFromUtf8()
{
    string utf8_String = "day’s";
    byte[] bytes = Encoding.Default.GetBytes(utf8_String);
    utf8_String = Encoding.UTF8.GetString(bytes);
}
Homem metódico
fonte
1
Obrigado! Funciona em espanhol, o problema é que o mesmo não funcionaria nas línguas orientais (ou seja, coreano). Estou tentando procurar um algoritmo de conversão de 8 bits para UTF-8 na internet, mas ainda sem sorte.
Gaara
Aqui está um exemplo de uma cadeia que não funciona no idioma Inglês: em vez de exibir dia, meu aplicativo front-end está exibindo: dayâ € ™ s
Gaara
ok, deixe-me mexer com ele e ver o que eu posso inventar.
MethodMan
Eu testei e ele retorna dia é vou colar o método estático que eu testei ele é realmente o mesmo que @anothershrubery forneceu
MethodMan
você pode alterar esse método passando DecodeFromUtf8 (string utf8string);
precisa
12

Seu código está lendo uma sequência de bytes codificados em UTF8 e decodificando-os usando uma codificação de 8 bits.

Você precisa corrigir esse código para decodificar os bytes como UTF8.

Como alternativa ( não ideal ), você pode converter a sequência incorreta de volta na matriz de bytes original - codificando-a usando a codificação incorreta - e decodificando novamente os bytes como UTF8.

SLaks
fonte
Obrigado! O problema é que o aplicativo de terceiros é C ++, enquanto meu código é C #, então acho que a decodificação acontece na "ponte" entre os dois.
Gaara
8
 Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(mystring));
Riadh Hammouda
fonte
5

Se você deseja salvar qualquer string no banco de dados mysql, faça o seguinte: ->

A estrutura do campo do banco de dados i phpmyadmin [ou qualquer outro painel de controle] deve ser definida como utf8-gerneral-ci

2) você deve alterar sua string [Ex. textbox1.text] para byte, portanto

2-1) define byte [] st2;

2-2) converta sua string [textbox1.text] em unicode [mmultibyte string]:

byte[] st2 = System.Text.Encoding.UTF8.GetBytes(textBox1.Text);

3) execute este comando sql antes de qualquer consulta:

string mysql_query2 = "SET NAMES 'utf8'";
cmd.CommandText = mysql_query2;
cmd.ExecuteNonQuery();

3-2) agora você deve inserir esse valor em, por exemplo, campo de nome:

cmd.CommandText = "INSERT INTO customer (`name`) values (@name)";

4) o principal trabalho que muitas soluções não deram atenção a ela é a linha abaixo: você deve usar addwithvalue em vez de add no parâmetro de comando como abaixo:

cmd.Parameters.AddWithValue("@name",ut);

++++++++++++++++++++++++++++++++++ Aproveite os dados reais no servidor de banco de dados em vez de ????

Hassan Fadaie Ghotbie
fonte
3

Use o trecho de código abaixo para obter bytes do arquivo csv

protected byte[] GetCSVFileContent(string fileName)
    {
        StringBuilder sb = new StringBuilder();
        using (StreamReader sr = new StreamReader(fileName, Encoding.Default, true))
        {
            String line;
            // Read and display lines from the file until the end of 
            // the file is reached.
            while ((line = sr.ReadLine()) != null)
            {
                sb.AppendLine(line);
            }
        }
        string allines = sb.ToString();


        UTF8Encoding utf8 = new UTF8Encoding();


        var preamble = utf8.GetPreamble();

        var data = utf8.GetBytes(allines);


        return data;
    }

Ligue para o abaixo e salve-o como um anexo

           Encoding csvEncoding = Encoding.UTF8;
                   //byte[] csvFile = GetCSVFileContent(FileUpload1.PostedFile.FileName);
          byte[] csvFile = GetCSVFileContent("Your_CSV_File_NAme");


        string attachment = String.Format("attachment; filename={0}.csv", "uomEncoded");

        Response.Clear();
        Response.ClearHeaders();
        Response.ClearContent();
        Response.ContentType = "text/csv";
        Response.ContentEncoding = csvEncoding;
        Response.AppendHeader("Content-Disposition", attachment);
        //Response.BinaryWrite(csvEncoding.GetPreamble());
        Response.BinaryWrite(csvFile);
        Response.Flush();
        Response.End();
jAntoni
fonte