2014-12-16 3 views
2

моей целью является преобразование строки .NET (Unicode) в Windows-1252 и - при необходимости - сохранение исходной строки UTF-8 в объекте Base64.Сравнение строки Windows-1252 с строкой UTF-8

Например, строка «DJ Doena», преобразованная в 1252, по-прежнему «DJ Doena».

Однако, если вы конвертируете японский kanjii для дерева (木) в 1251, вы получите знак вопроса.

Это мои тестовые строки:

String doena = "DJ Doena"; 
String umlaut = "äöüßéèâ"; 
String allIn = "< ä ß á â & 木 >"; 

Это как преобразовать строку в первую очередь:

using (MemoryStream ms = new MemoryStream()) 
{ 
    using (StreamWriter sw = new StreamWriter(ms, Encoding.UTF8)) 
    { 
     sw.Write(decoded); 
     sw.Flush(); 
     ms.Seek(0, SeekOrigin.Begin); 
     using (StreamReader sr = new StreamReader(ms, Encoding.GetEncoding(1252))) 
     { 
      encoded = sr.ReadToEnd(); 
     } 
    } 
} 

Проблема, при отладке сравнения строк утверждает, что оба действительно идентичны, поэтому простого == или .Equals() не хватает.

Это, как я пытаюсь выяснить, если мне нужно base64 и произвести его:

private static String GetBase64Alternate(String utf8Text, String windows1252Text) 
{ 
    Byte[] utf8Bytes; 
    Byte[] windows1252Bytes; 
    String base64; 

    utf8Bytes = Encoding.UTF8.GetBytes(utf8Text); 
    windows1252Bytes = Encoding.GetEncoding(1252).GetBytes(windows1252Text); 
    base64 = null; 
    if (utf8Bytes.Length != windows1252Bytes.Length) 
    { 
     base64 = Convert.ToBase64String(utf8Bytes); 
    } 
    else 
    { 
     for(Int32 i = 0; i < utf8Bytes.Length; i++) 
     { 
      if(utf8Bytes[i] != windows1252Bytes[i]) 
      { 
       base64 = Convert.ToBase64String(utf8Bytes); 
       break; 
      } 
     } 
    } 
    return (base64); 
} 

Первая строка doena полностью идентична и не дает в результате Base64

Console.WriteLine(String.Format("{0}/{1}", windows1252Text, base64Text)); 

результаты в

DJ Doena/

Но вторая строка umlauts a lready имеет в два раза больше байт в кодировке UTF-8, чем в 1252 году, и, таким образом, создает строку Base64, даже если это не представляется необходимым:

äöüßéèâ/w6TDtsO8w5/DqcOow6I= 

И третий один делает то, что он должен не делать (не более «木? ", а„“, таким образом, base64 требуется):

< ä ß á â & ? >/PCDDpCDDnyDDoSDDoiAmIOacqCA+ 

Любые подсказки, как мой Base64 геттер может быть повышена а) для исполнения б) для достижения лучших результатов?

Заранее спасибо. :-)

+1

В качестве примечания: Рассмотрим нормализацию в FormC перед кодированием, как Windows-1252. – CodesInChaos

ответ

3

Я не уверен, что полностью понял вопрос. Но я попробовал. :) Если я правильно понимаю, этот код делает то, что вы хотите:

static void Main(string[] args) 
{ 
    string[] testStrings = { "DJ Doena", "äöüßéèâ", "< ä ß á â & 木 >" }; 

    foreach (string text in testStrings) 
    { 
     Console.WriteLine(ReencodeText(text)); 
    } 
} 

private static string ReencodeText(string text) 
{ 
    Encoding encoding = Encoding.GetEncoding(1252); 
    string text1252 = encoding.GetString(encoding.GetBytes(text)); 

    return text.Equals(text1252, StringComparison.Ordinal) ? 
     text : Convert.ToBase64String(Encoding.UTF8.GetBytes(text)); 
} 

I.e. он кодирует текст в Windows-1252, затем декодирует обратно объект string, который затем сравнивается с оригиналом. Если сравнение выполнено успешно, оно возвращает исходную строку, в противном случае она кодирует ее в UTF8, а затем в base64.

Он производит следующий вывод:

DJ Doena
äöüßéèâ
PCDDpCDDnyDDoSDDoiAmIOacqCA +

Другими словами, первые две строки остаются нетронутыми, в то время как третья кодируется как base64.

+0

Одна из проблем заключается в том, что невозможно определить, была ли строка закодирована Base64 или если она оригинала. – CodesInChaos

+0

Тривиально изменить приведенный выше пример, чтобы передать эту информацию обратно вызывающему абоненту, если это необходимо. Я бы подумал, что простое сравнение возвращаемого значения с переданным значением будет достаточным в большинстве случаев (оно не будет работать, если OP имеет дело со входными строками, которые являются _already_ base64). –

+0

Спасибо, это было именно то, что я пытался достичь! –

1

В вашем первом коде вы кодируете строку с использованием одной кодировки, а затем декодируете ее с использованием другой кодировки. Это не дает вам никакого надежного результата; это эквивалент выписывания числа в восьмеричном, а затем его чтение, как если бы оно было десятичным. Кажется, что это нормально для чисел до 7, но после этого вы получаете бесполезные результаты.

Проблема заключается в том, что она кодирует строку в два разных кодирования и предполагает,

Сравнение последовательностей байтов не говорит о сбоях в работе каких-либо кодов. Последовательность будет отличаться, если она не удалась, но она также будет отличаться, если есть какие-либо символы, которые кодируются по-разному между кодировками.

Что вы хотите сделать, так это определить, действительно ли кодировка работает для всех символов. Это можно сделать, создав экземпляр Encoding с резервным копированием неподдерживаемых символов. Существует класс EncoderExceptionFallback, который вы можете использовать для этого, который выдает EncoderFallbackException, если он вызван.

Этот код будет пытаться использовать кодировку Windows-1252 на шнурке, и устанавливает переменную ok в false, если кодировка не поддерживает все символы в строке:

Encoding e = Encoding.GetEncoding(1252, new EncoderExceptionFallback(), new DecoderExceptionFallback()); 
bool ok = true; 
try { 
    e.GetByteCount(allIn); 
} catch (EncoderFallbackException) { 
    ok = false; 
} 

Как вы на самом деле не для использования закодированного результата для чего угодно, вы можете использовать метод GetByteCount. Он проверит, как все символы будут закодированы без создания закодированного результата.

Используется в методе было бы:

private static String GetBase64Alternate(string text) { 
    Encoding e = Encoding.GetEncoding(1252, new EncoderExceptionFallback(), new DecoderExceptionFallback()); 
    bool ok = true; 
    try { 
    e.GetByteCount(allIn); 
    } catch (EncoderFallbackException) { 
    ok = false; 
    } 
    return ok ? null : Convert.ToBase64(Encoding.UTF8.GetBytes(text)); 
}