Jak mogę przekształcić ciąg do UTF-8 w C #?

146

Mam ciąg, który otrzymałem z aplikacji innej firmy i chciałbym wyświetlić go poprawnie w dowolnym języku przy użyciu języka C # na moim Windows Surface.

Z powodu nieprawidłowego kodowania fragment mojego ciągu wygląda tak po hiszpańsku:

Acción

podczas gdy powinno to wyglądać tak:

Acción

Zgodnie z odpowiedzią na to pytanie: Jak poznać kodowanie ciągów w C # , kodowanie, które otrzymuję, powinno już pochodzić z UTF-8, ale jest odczytywane na Encoding.Default (prawdopodobnie ANSI?).

Próbuję przekształcić ten ciąg w prawdziwy UTF-8, ale jednym z problemów jest to, że widzę tylko podzbiór klasy Encoding (tylko właściwości UTF8 i Unicode), prawdopodobnie dlatego, że jestem ograniczony do interfejsu API powierzchni systemu Windows.

Wypróbowałem kilka fragmentów, które znalazłem w Internecie, ale żaden z nich nie okazał się jak dotąd skuteczny dla języków wschodnich (tj. Koreańskiego). Oto jeden przykład:

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

Próbowałem również wyodrębnić ciąg do tablicy bajtów, a następnie użyć 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);

Czy macie jakieś inne pomysły, które mógłbym wypróbować?

Gaara
źródło
5
Twój problem pochodzi z kodu, który utworzył łańcuch (ze strumienia lub bajtu []) w pierwszej kolejności. Proszę pokazać ten kod.
SLaks
1
@Oded: .Net strings są przechowywane w pamięci jako UTF16, ale Encoding.Defaultzwraca stronę kodową ANSI systemu.
Slaks
Oto przykład ciągu znaków, który nie działa w języku angielskim: zamiast wyświetlać dni, moja aplikacja front-end wyświetla: dzień
Gaara

Odpowiedzi:

251

Jak wiesz, nadchodzi ciąg znaków, ponieważ Encoding.Defaultmożesz po prostu użyć:

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

Kolejna rzecz, o której być może będziesz musiał pamiętać: jeśli używasz Console.WriteLine do wypisywania jakichś łańcuchów, to również powinieneś napisać Console.OutputEncoding = System.Text.Encoding.UTF8;!!! Lub wszystkie ciągi utf8 zostaną wyprowadzone jako gbk ...

inny krzew
źródło
To też działa, w rzeczywistości jest o wiele ładniejsze niż moja odpowiedź, która również działa
Daję
Dzięki! Problem w tym, że jak wspomniałem w opisie, API dla powierzchni jest niekompletne (dla mnie brak Encoding.Default).
Gaara
3
@Gaara: Spróbuj Encoding.GetEncoding(...); będziesz musiał znaleźć nazwę rzeczywistego kodowania, które zostało nieprawidłowo użyte na drugim końcu.
SLaks
1
czy możesz wyjaśnić, dlaczego to działa? jeśli wartość domyślna to GB2312, Encoding.Default.GetBytes zakoduje ciąg do tablicy bajtów za pomocą kodera GB2312, a następnie Encoding.UTF8.GetString spróbuje zdekodować tablicę bajtów za pomocą dekodera UTF8, wynik powinien być nieprawidłowy, ale dlaczego to działa. @anothershrubery
guorongfei
1
@guorongfei Założeniem jest to, że myStringjest to mojibake. Kod najpierw cofa nieprawidłowe dekodowanie, a następnie wykonuje właściwe dekodowanie. Działa, o ile niewłaściwe dekodowanie nie powoduje utraty danych. Ale jak zauważył @SLaks, lepiej byłoby użyć dokładnego kodowania, które było nieprawidłowe. (Lepsze nazwy i komentarze w kodzie pomogłyby w zrozumieniu, jak bardzo źle wyglądający kod jest w rzeczywistości próbą wykonania dobrze.)
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);

Wynik powinien wyglądać jak

Acción

dzień wyświetla dzień

wywołanie DecodeFromUtf8 ();

private static void DecodeFromUtf8()
{
    string utf8_String = "day’s";
    byte[] bytes = Encoding.Default.GetBytes(utf8_String);
    utf8_String = Encoding.UTF8.GetString(bytes);
}
MethodMan
źródło
1
Dzięki! Działa po hiszpańsku, problem polega na tym, że to samo nie działałoby w językach wschodnich (tj. Koreańskim). Próbuję poszukać algorytmu konwersji 8-bitowego na UTF-8 w Internecie, ale nadal nie mam szczęścia.
Gaara
Oto przykład ciągu znaków, który nie działa w języku angielskim: zamiast wyświetlać dni, moja aplikacja front-end wyświetla: dzień
Gaara
ok, pozwól mi się z tym
bawić
Przetestowałem i zwraca dzień, wkleję metodę statyczną, którą przetestowałem, jest właściwie taka sama, jak to, co dostarczyła
@anothershrubery
możesz zmienić tę metodę, przekazując DecodeFromUtf8 (string utf8string);
MethodMan
12

Twój kod odczytuje sekwencję bajtów zakodowanych w formacie UTF8 i dekoduje je przy użyciu kodowania 8-bitowego.

Musisz naprawić ten kod, aby zdekodować bajty jako UTF8.

Alternatywnie ( nie jest to idealne rozwiązanie ), możesz przekonwertować zły ciąg z powrotem na oryginalną tablicę bajtów - przez zakodowanie go przy użyciu nieprawidłowego kodowania - a następnie ponownie zdekodować bajty jako UTF8.

SLaks
źródło
Dzięki! Problem polega na tym, że aplikacja innej firmy to C ++, a mój kod to C #, więc domyślam się, że dekodowanie odbywa się w „pomoście” między tymi dwoma.
Gaara
8
 Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(mystring));
Riadh Hammouda
źródło
5

Jeśli chcesz zapisać dowolny ciąg w bazie danych mysql, zrób to: ->

Struktura pól Twojej bazy danych i phpmyadmin [lub jakikolwiek inny panel sterowania] powinna być ustawiona na utf8-gerneral-ci

2) powinieneś zmienić swój ciąg [np. textbox1.text] na bajt

2-1) zdefiniuj bajt [] st2;

2-2) przekonwertuj swój ciąg [textbox1.text] na Unicode [mmultibyte string] przez:

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

3) wykonaj to polecenie sql przed każdym zapytaniem:

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

3-2) należy teraz wstawić tę wartość do np. Pola nazwy poprzez:

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

4) głównym zadaniem, na które wiele rozwiązań nie zwracało uwagi, jest poniższa linia: powinieneś użyć addwithvalue zamiast dodawać parametr polecenia, jak poniżej:

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

++++++++++++++++++++++++++++++++++ ciesz się prawdziwymi danymi na serwerze bazy danych zamiast ????

Hassan Fadaie Ghotbie
źródło
3

Użyj poniższego fragmentu kodu, aby pobrać bajty z pliku 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;
    }

Zadzwoń poniżej i zapisz jako załącznik

           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
źródło