Работа со строками из внешних источников

В пределах .NET Framework все строки представлены массивами символов Unicode UTF-16. Однако часто возникает необходимость взаимодействия с внешним миром, использующим какую-то другую форму кодирования, например, UTF-8.

Иногда даже взаимодействуя с другими сущностями, использующими 16-битные строки Unicode, может случиться, что в них применяется порядок следования байтов, начиная со старшего, который противоположен принятому на платформе Intel порядку следования, начиная с младшего байта.

В среде .NET Framework решение этой задачи упрощается за счет использования класса System.Text.Encoding.

В этом посте не приводится детальное описание System.Text.Encoding. За подробной информацией об этом классе обращайтесь к документации MSDN. Ниже приведен краткий пример преобразования строк между разными кодировками с применением объектов Encoding, обеспечиваемыми классом System.Text.Encoding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using System;
using System.Text;
public class EntryPoint {
static void Main()  {
string leUnicodeStr = "здорово!";
Encoding leUnicode = Encoding.Unicode;
Encoding beUnicode = Encoding.BigEndianUnicode;
Encoding utf8 = Encoding.UTF8;
byte[] leUnicodeBytes = leUnicode.GetBytes(leUnicodeStr);
byte[] beUnicodeBytes = Encoding.Convert ( leUnicode,
beUnicode, leUnicodeBytes);
byte[] utf8Bytes = Encoding.Convert ( leUnicode,
utf8,
leUnicodeBytes ) ;
Console.WriteLine ( "Исходная строка:  {0}\n", leUnicodeStr );
Console.WriteLine ( "Байты Unicode, сначала младший:" );
StringBuilder sb = new StringBuilder();
foreach( byte b in leUnicodeBytes )  {
sb. Append ( b ) .Append (" : ") ;
}
Console.WriteLine ( "{0}\n", sb.ToString() );
Console.WriteLine ( "Байты Unicode, сначала старший:" );
sb = new StringBuilder();
foreach( byte b in beUnicodeBytes )  {
sb.Append( b ).Append(" : ");
}
Console.WriteLine ( "{0}\n", sb.ToString () );
Console.WriteLine ( "Байты UTF:" );
sb = new StringBuilder();
foreach ( byte b in utf8Bytes )  {
sb.Append( b ).Append(" : ");
}
Console.WriteLine ( sb.ToString() );

В коде примера сначала создается System.String с некоторым текстом на русском языке. Как упоминалось ранее, строка содержит строку Unicode, но в каком порядке идут байты символа — сначала старший или сначала младший? Ответ зависит от платформы, на которой запускается код.

В системе Intel обычно сначала идет младший байт. Однако поскольку доступ к лежащему в основе байтовому представлению строк отсутствует, так как оно скрыто, это не имеет значения. Для получения байтов строки используется один из объектов Encoding, которые получается от System.Text.Encoding.

В рассмотренном примере получаются локальные ссылки на объекты Encoding для обработки строк Unicode с первым старшим байтом, первым младшим байтом и UTF-8.

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

Если бы исходная строка базировалась на латинском наборе символов, то массив UTF-8 получился бы короче, чем массив Unicode — обычно наполовину. Главное, что следует запомнить: никогда не нужно строить предположения относительно требований по хранению для любой из кодировок. Чтобы узнать, сколько места потребуется для хранения кодированной строки, вызовите метод Encoding.GetByteCount.

Никогда не стройте предположений относительно внутреннего формата представления строк в CLR. Нет никаких гарантий того, что оно не будет варьироваться от одной платформы к другой. Будет весьма неприятно, если вдруг код сделает какое-то предположение, отталкиваясь от платформы Intel, а затем даст сбой при запуске на платформе Sun с функционирующей средой Mono CLR.

Однажды в Microsoft могут даже решить перенести Windows на другую платформу — точно так же, как в Apple приняли решение об использовании процессоров Intel. То, что объект Encoding.Unicode не называется, скажем, Encoding.LittleEndianUnicode, не означает, что CLR представляет внутри себя все строковые данные в формате с первым младшим байтои.

На самом деле стандарт CLI ясно указывает, что все типы данных, занимающие более одного байта в памяти, представлены в порядке, зависящем от целевой платформы.

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

Например, стек протоколов Bluetooth использует кодировку Unicode с первым старшим байтом для передачи строковых данных. Чтобы преобразовать байты в System.String, вызывайте метод GetString на применяемом кодировщике. Используемый кодировщик должен соответствовать источнику кодируемых данных.

Это приводит к важному замечанию, которое всегда надо иметь в виду. При передаче строки в другие системы и обратно в низкоуровневом байтовом формате необходимо знать используемую протоколом схему кодирования. Очень важно, что для преобразования байтового массива в System.String всегда должен применяться соответствующий кодировке объект Encoding, даже если известно, что в протоколе используется точно такая же кодировка, как и внутри System.String на платформе, на которой строится приложение.

Почему?

Предположим, что приложение разрабатывается на платформе Intel, где протокол кодирования предполагает, что в паре байт символа первым идет младший, при этом известно, что такой же порядок принят на целевой платформе. Вы решаете сэкономить, и отказываетесь от применения объекта System.Text.Encoding.Unicode для преобразования байтов строки.

Позднее вы запускаете приложение на платформе, которая внутри использует порядок байтов со старшим байтом первым. Приложение начнет сбоить из-за того, что было выдвинуто ошибочное предположение относительно внутренней реализации кодирования System.String. Постоянное использование кодировщика не повлияет на эффективность, потому что на платформе, где внутреннее кодирование совпадает с внешним, лишние преобразования не выполняются.

В предыдущем примере для вывода массива байтов на консоль применялся класс StringBuilder. Давайте посмотрим, что он собой представляет.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100