Типы данных в Си Шарп
C# — это типизированный язык. То есть при объявлении переменных необходимо указывать для них тип данных.
Типы данных в Си Шарп, как и в других языках, можно разделить на несколько групп. Но сначала попробую сгруппировать типы без привязки к какому-либо языку:
- Порядковые (их также можно назвать простыми). Это самые простые типы, такие как целые числа и отдельные символы.
- Вещественные. Это вещественные (действительные) числа, то есть числа с дробной частью.
- Сложные (ну это я их так называю). Сюда можно включить, например, массивы, строки, файлы и т.п.
- Пользовательские. Ну а сюда я включаю всё остальное — классы, объекты, структуры…
Начинать будем с порядковых (но не сегодня — сегодня только вводная часть), суть которых я объяснил здесь, поэтому повторяться не буду (правда, там речь идёт не о C#, а о Паскале, но в данном случае это значения не имеет).
А теперь чуть подробнее состав типов данных в моём видении. Итак…
Простые типы данных
Сюда можно отнести такие типы данных, как числовые (целые числа), логические, а также символьные (но не строковые, хотя строковые типы обычно тоже относят к простым, но они не могут быть порядковыми).
Вроде как для целых чисел достаточно одного типа данных. Однако в компьютерном мире всё не так просто. Например, если бы в языке был только один тип данных, то нам пришлось бы делать его с большим запасом, чтобы в него поместились любые числа, в том числе и очень большие. И нам пришлось бы для каждого числа выделять 4 или даже 8 байтов, даже если мы уверены, что значение нашей переменной всегда поместится в один байт.
К тому же числа могут быть как отрицательными, так и положительными. И не всегда в программе какая-то переменная может принимать и отрицательное, и положительное значение. Много случаев, когда мы точно знаем, что значение переменной не будет отрицательным на протяжении всей программы.
Поэтому в языках программирования для целых чисел всегда имеется несколько типов данных. И C# не исключение.
Вещественные типы данных
Далеко не всегда, особенно в различных вычислениях, можно обойтись целыми числами. Поэтому, конечно, в любом языке программирования имеются вещественные типы данных, которые могут работать с действительными числами (числами с дробной частью).
Таких типов в языке обычно намного меньше, чем целочисленных. А на практике обычно используются лишь 2-3 типа.
Сложные типы данных
Сложными я их называю потому, что новичкам они обычно даются труднее для понимания. Например, к сложным типам я отношу указатели, но не потому, что этот тип сложный, а потому, что новичкам часто трудно понять, как с указателями работать.
К сложным я отношу и строки (хотя это самое простое из сложных типов), массивы, множества, файлы — такие типы есть почти во всех языках программирования.
Далее идут типы, которые могут быть похожими, но названия их могут отличаться в зависимости от языка. Также у некоторых языков есть свои уникальные типы данных (хотя такие обычно используются редко).
Это могут быть коллекции (в C#), словари, списки, какие-то особые типы для особых случаев (например, для работы с комплексными числами или с матрицами) и т.п.
Пользовательские типы данных
Пользовательские типы данных — это типы, которые создаёт сам пользователь (точнее, программист — пользователь языка). Например, это могут быть перечисления, или структуры (записи в Паскале). Также классы и объекты могут быть созданы пользователем (хотя существует и немало данных этих типов, встроенных в язык).
Типы данных в C#
Ну а если говорить не о языках программирования вообще, а именно о Си Шарп, то в литературе, как это не удивительно, очень редко встречается классификация типов. Но я нашёл вот такую, от которой, пожалуй, можно оттолкнуться…
В C# существует две разновидности типов переменных, каждая из которых содержит несколько “подразновидностей”:
- Типы-значения:
- простые
- перечисляемые
- структурные
- обнуляемые
- классы
- интерфейсы
- массивы
- делегаты
Переменные типа-значения содержат значения, а в переменных ссылочного типа (объектах) хранятся ссылки на данные.
Си Шарп имеет единую систему типов, так что значение любого типа может использоваться как объект. Любой тип в C# прямо или косвенно наследуется от типа object , и object является исходным базовым классом всех типов. Значения ссылочных типов рассматриваются как объекты просто путем их представления как величин типа object . Значения типов-значений преобразуются в объекты с помощью выполнения операций упаковки и распаковки.
Но это уже другая история. А вводную часть на этом закончим. Подписывайтесь на новости, чтобы ничего не пропустить (ссылки внизу).
Как пропустить байты в строке си шарп
Особый класс операций представляют поразрядные операции. Они выполняются над отдельными разрядами числа. В этом плане числа рассматриваются в двоичном представлении, например, 2 в двоичном представлении 10 и имеет два разряда, число 7 — 111 и имеет три разряда.
Логические операции
- & (логическое умножение) Умножение производится поразрядно, и если у обоих операндов значения разрядов равно 1, то операция возвращает 1, иначе возвращается число 0. Например:
int x1 = 2; //010 int y1 = 5;//101 Console.WriteLine(x1&y1); // выведет 0 int x2 = 4; //100 int y2 = 5; //101 Console.WriteLine(x2 & y2); // выведет 4
int x1 = 2; //010 int y1 = 5;//101 Console.WriteLine(x1|y1); // выведет 7 - 111 int x2 = 4; //100 int y2 = 5;//101 Console.WriteLine(x2 | y2); // выведет 5 - 101
int x = 45; // Значение, которое надо зашифровать - в двоичной форме 101101 int key = 102; //Пусть это будет ключ - в двоичной форме 1100110 int encrypt = x ^ key; //Результатом будет число 1001011 или 75 Console.WriteLine($"Зашифрованное число: ") ; int decrypt = encrypt ^ key; // Результатом будет исходное число 45 Console.WriteLine($"Расшифрованное число: ");
int x = 12; // 00001100 Console.WriteLine(~x); // 11110011 или -13
Представление отрицательных чисел
Для записи чисел со знаком в C# применяется дополнительный код (two’s complement), при котором старший разряд является знаковым. Если его значение равно 0, то число положительное, и его двоичное представление не отличается от представления беззнакового числа. Например, 0000 0001 в десятичной системе 1.
Если старший разряд равен 1, то мы имеем дело с отрицательным числом. Например, 1111 1111 в десятичной системе представляет -1. Соответственно, 1111 0011 представляет -13.
Чтобы получить из положительного числа отрицательное, его нужно инвертировать и прибавить единицу:
int x = 12; int y = ~x; y += 1; Console.WriteLine(y); // -12

Операции сдвига
Операции сдвига также производятся над разрядами чисел. Сдвиг может происходить вправо и влево.
Таким образом, если исходное число, которое надо сдвинуть в ту или другую строну, делится на два, то фактически получается умножение или деление на два. Поэтому подобную операцию можно использовать вместо непосредственного умножения или деления на два. Например:
int a = 16; // в двоичной форме 10000 int b = 2; // в двоичной форме int c = a ") ; // 64 int d = a >> b; // Сдвиг числа 10000 вправо на 2 разряда, равно 100 или 4 в десятичной системе Console.WriteLine($"Зашифрованное число: "); // 4
При этом числа, которые участвую в операциях, необязательно должны быть кратны 2:
int a = 22; // в двоичной форме 10110 int b = 2; // в двоичной форме int c = a ") ; // 88 int d = a >> b; // Сдвиг числа 10110 вправо на 2 разряда, равно 101 или 5 в десятичной системе Console.WriteLine($"Зашифрованное число: "); // 5
Пример практического применения операций
Многие недооценивают поразрядные операции, не понимают, для чего они нужны. Тем не менее они могут помочь в решении ряда задач. Прежде всего они позволяют нам манипулировать данными на уровне отдельных битов. Один из примеров. У нас есть три числа, которые находятся в диапазоне от 0 до 3:
int value1 = 3; // 0b0000_0011 int value2 = 2; // 0b0000_0010 int value3 = 1; // 0b0000_0001
Мы знаем, что значения этих чисел не будут больше 3, и нам нужно эти данные максимально сжать. Мы можем три числа сохранить в одно число. И в этом нам помогут поразрядные операции.
int value1 = 3; // 0b0000_0011 int value2 = 2; // 0b0000_0010 int value3 = 1; // 0b0000_0001 int result = 0b0000_0000; // сохраняем в result значения из value1 result = result | value1; // 0b0000_0011 // сдвигаем разряды в result на 2 разряда влево result = result
Разберем этот код. Сначала определяем все сохраняемые числа value1, value2, value3. Для хранения результата определена переменная result, которая по умолчанию равна 0. Для большей наглядности ей присвоено значение в бинарном формате:
int result = 0b0000_0000;
Сохраняем первое число в result:
result = result | value1; // 0b0000_0011
Здесь мы имеем дело с логической операцией поразрядного сложения - если один из соответствующих разрядов равен 1, то результирующий разряд тоже будет равен 1. То есть фактически
0b0000_0000 + 0b0000_0011 = 0b0000_0011
Итак, первое число сохранили в result. Мы будем сохранять числа по порядку. То есть сначала в result будет идти первое число, затем второе и далее третье. Поэтому сдвигаем число result на два разряда влево (наши числа занимают в памяти не более двух разрядов):
result = result
То есть фактически
0b0000_0011
Далее повторяем логическую операцию сложения, сохраняем второе число:
result = result | value2; // 0b0000_1110
0b0000_1100 + 0b0000_0010 = 0b0000_1110
Далее повторяем сдвиг на два разряда влево и сохраняем третье число. В итоге мы получим в двоичном представлении число 0b0011_1001 . В десятично системе это число равно 57. Но это не имеет значения, потому что нам важны конкретные биты числа. Стоит отметить, что мы сохранили в одно число три числа, и в переменной result еще есть сводобное место. Причем в реальности не важно, сколько именно битов надо сохранить. В данном случае для примера сохраняем лишь два бита.
Для восстановления данных прибегнем к обратному порядку:
result = 0b0011_1001 // обратное получение данных int newValue3 = result & 0b000_0011; // сдвигаем данные на 2 разряда вправо result = result >> 2; int newValue2 = result & 0b000_0011; // сдвигаем данные на 2 разряда вправо result = result >> 2; int newValue1 = result & 0b000_0011; Console.WriteLine(newValue1); // 3 Console.WriteLine(newValue2); // 2 Console.WriteLine(newValue3); // 1
Получаем числа в порядке, обратном тому, в котором они были сохранены. Поскольку мы знаем, что каждое сохраненное число занимает лишь два разряда, то по сути нам надо получить лишь последние два бита. Для этого применяем битовую маску 0b000_0011 и операцию логического умножения, которая возвращает 1, если каждый из двух соответствующих разрядов равен 1. То есть операция
int newValue3 = result & 0b000_0011;
0b0011_1001 * 0b0000_0011 = 0b0000_0001
Таким образом, последнее число равно 0b0000_0001 или 1 в десятичной системе
Стоит отметить, что если мы точно знаем структуру данных, то мы легко можем составить битовую маску, чтобы получить нужно число:
result = 0b0011_1001; int recreatedValue1 = (result & 0b0011_0000) >> 4; Console.WriteLine(recreatedValue1);
Здесь получаем первое число, которое, как мы знаем, занимает в числе биты 4 и 5. Для этого применяем умножение на битовую маску 0b0011_0000. И затем сдвигаем число на 4 разряда вправо.
0b0011_1001 * 0b0011_0000 = 0b0011_0000 >> 4 = 0b0000_0011
Аналогично, если мы точно знаем структуру, по которой сохраняются данные, то мы могли бы сохранить данные сразу в нужное место в числе result:
int value1 = 3; // 0b0000_0011 int value2 = 2; // 0b0000_0010 int value3 = 1; // 0b0000_0001 int result = 0b0000_0000; // сохраняем в result значения из value1 result = result | (value1
Как пропустить байты в строке си шарп
Циклы являются управляющими конструкциями, позволяя в зависимости от определенных условий выполнять некоторое действие множество раз. В C# имеются следующие виды циклов:
Цикл for
Цикл for имеет следующее формальное определение:
for ([действия_до_выполнения_цикла]; [условие]; [действия_после_выполнения]) < // действия >
Объявление цикла for состоит из трех частей. Первая часть объявления цикла - некоторые действия, которые выполняются один раз до выполнения цикла. Обычно здесь определяются переменные, которые будут использоваться в цикле.
Вторая часть - условие, при котором будет выполняться цикл. Пока условие равно true , будет выполняться цикл.
И третья часть - некоторые действия, которые выполняются после завершения блока цикла. Эти действия выполняются каждый раз при завершении блока цикла.
После объявления цикла в фигурных скобках помещаются сами действия цикла.
Рассмотрим стандартный цикл for:
for (int i = 1; i
Здесь первая часть объявления цикла - int i = 1 - создает и инициализирует переменную i.
И третья часть - действия, выполняемые после завершения действий из блока цикла - увеличение переменной i на единицу.
Весь процесс цикла можно представить следующим образом:
В итоге блок цикла сработает 3 раза, пока значение i не станет равным 4. И каждый раз это значение будет увеличиваться на 1. Однократное выполнение блока цикла называется итерацией . Таким образом, здесь цикл выполнит три итерации. Результат работы программы:
1 2 3
Если блок цикла for содержит одну инструкцию, то мы можем его сократить, убрав фигурные свобки:
for (int i = 1; i < 4; i++) Console.WriteLine(i); // или так for (int i = 1; i < 4; i++) Console.WriteLine(i);
При этом необязательно именно в первой части цикла объявлять переменную, а в третий части изменять ее значение - это могут быть любые действия. Например:
var i = 1; for (Console.WriteLine("Начало выполнения цикла"); i < 4; Console.WriteLine($"i = "))
Здесь опять же цикл срабатывает, пока переменная i меньше 4, только приращение переменной i происходит в блоке цикла. Консольный вывод данной программы:
Начало выполнения цикла i = 2 i = 3 i = 4
Нам необязательно указывать все условия при объявлении цикла. Например, мы можем написать так:
int i = 1; for (; ;) < Console.WriteLine($"i = "); i++; >
Формально определение цикла осталось тем же, только теперь блоки в определении у нас пустые: for (; 😉 . У нас нет инициализированной переменной, нет условия, поэтому цикл будет работать вечно - бесконечный цикл.
Мы также можем опустить ряд блоков:
int i = 1; for (; i<4;) < Console.WriteLine($"i = "); i++; >
Этот пример по сути эквивалентен первому примеру: у нас также есть переменная-счетчик, только определена она вне цикла. У нас есть условие выполнения цикла. И есть приращение переменной уже в самом блоке for.
Также стоит отметить, что можно определять несколько переменных в объявлении цикла:
for (int i = 1, j = 1; i < 10; i++, j++) Console.WriteLine($"");
Здесь в первой части объявления цикла определяются две переменных: i и j. Цикл выполняется, пока i не будет равна 10. После каждой итерации переменые i и j увеличиваются на единицу. Консольный вывод программы:
1 4 9 16 25 36 49 64 81
Цикл do..while
В цикле do сначала выполняется код цикла, а потом происходит проверка условия в инструкции while. И пока это условие истинно, цикл повторяется.
do < действия цикла >while (условие)
int i = 6; do < Console.WriteLine(i); i--; >while (i > 0);
Здесь код цикла сработает 6 раз, пока i не станет равным нулю. Но важно отметить, что цикл do гарантирует хотя бы единократное выполнение действий, даже если условие в инструкции while не будет истинно. То есть мы можем написать:
int i = -1; do < Console.WriteLine(i); i--; >while (i > 0);
Хотя у нас переменная i меньше 0, цикл все равно один раз выполнится.
Цикл while
В отличие от цикла do цикл while сразу проверяет истинность некоторого условия, и если условие истинно, то код цикла выполняется:
while (условие)
int i = 6; while (i > 0)Цикл foreach
Цикл foreach предназначен для перебора набора или коллекции элементов. Его общее определение:
foreach(тип_данных переменная in коллекция) < // действия цикла >
После оператора foreach в скобках сначала идет определение переменной. Затем ключевое слово in и далее коллекция, элементы которой надо перебрать.
При выполнении цикл последовательно перебирает элементы коллекции и помещает их в переменную, и таким образом в блоке цикла мы можем выполнить с ними некоторые действия.
Например, возьмем строку. Строка по сути - это коллекция символов. И .NET позволяет перебрать все элементы строки - ее символы с помощью цикла foreach .
foreach(char c in "Tom")
Здесь цикл foreach пробегается по всем символам строки "Tom" и каждый символ помещает в символьную переменную c . В блоке цикла значение переменной c выводится на консоль. Поскольку в строке "Tom" три символа, то цикл выполнится три раза. Консольный вывод программы:
T o m
Стоит отметить, что переменная, которая определяется в объявлении цикла, должна по типу соответствовать типу элементов перебираемой коллекции. Так, элементы строки - значения типа char - символы. Поэтому переменная c имеет тип char . Однако в реальности не всегда бывает очевидно, какой тип представляют элементы коллекции. В этом случае мы можем определить переменную с помощью оператора var :
foreach(var c in "Tom")
В дальнейшем мы подробнее рассмотрим, что представляют собой коллекции в .NET и какие именно коллекции можно перебирать с помощью цикла foreach .
Операторы continue и break
Иногда возникает ситуация, когда требуется выйти из цикла, не дожидаясь его завершения. В этом случае мы можем воспользоваться оператором break .
for (int i = 0; i
Хотя в условии цикла сказано, что цикл будет выполняться, пока счетчик i не достигнет значения 9, в реальности цикл сработает 5 раз. Так как при достижении счетчиком i значения 5, сработает оператор break, и цикл завершится.
0 1 2 3 4
Теперь поставим себе другую задачу. А что если мы хотим, чтобы при проверке цикл не завершался, а просто пропускал текущую итерацию. Для этого мы можем воспользоваться оператором continue :
for (int i = 0; i
В этом случае цикл, когда дойдет до числа 5, которое не удовлетворяет условию проверки, просто пропустит это число и перейдет к следующей итерации:
0 1 2 3 4 6 7 8
Стоит отметить, что операторы break и continue можно применять в любом типе циклов.
Вложенные циклы
Одни циклы могут быть вложенными в другие. Например:
for (int i = 1; i < 10; i++) < for (int j = 1; j < 10; j++) < Console.Write($"\t"); > Console.WriteLine(); >
- Вопросы для самопроверки
- Упражнения
- Глава 1. Введение в C#
- Язык C# и платформа .NET
- Первая программа на C# с .NET CLI
- Начало работы с Visual Studio. Первая программа
- Первая программа на MacOS
- Первая программа на Linux
- Первое приложение в WSL
- Структура программы
- Переменные и константы
- Литералы
- Типы данных
- Консольный ввод-вывод
- Арифметические операции
- Поразрядные операции
- Операции присваивания
- Преобразования базовых типов данных
- Условные выражения
- Конструкция if..else и тернарная операция
- Циклы
- Массивы
- Задачи с массивами
- Методы
- Параметры методов
- Возвращение значения и оператор return
- Передача параметров по ссылке и значению. Выходные параметры
- Массив параметров и ключевое слово params
- Рекурсивные функции
- Локальные функции
- Конструкция switch
- Перечисления enum
- Классы и объекты
- Конструкторы, инициализаторы и деконструкторы
- Класс Program и метод Main. Программы верхнего уровня
- Структуры
- Типы значений и ссылочные типы
- Область видимости (контекст) переменных
- Пространства имен
- Глобальные пространства имен
- Подключение пространств имен по умолчанию
- Создание библиотеки классов в Visual Studio
- Создание библиотеки классов с помощью .NET CLI
- Модификаторы доступа
- Свойства
- Перегрузка методов
- Статические члены и модификатор static
- Установка пакетов Nuget
- Константы, поля и структуры для чтения
- Null и ссылочные типы
- Null и значимые типы
- Проверка на null, операторы ?. и ??
- Псевдонимы типов и статический импорт
- Наследование
- Преобразование типов
- Виртуальные методы и свойства
- Скрытие методов и свойств
- Различие переопределения и скрытия методов
- Абстрактные классы
- Класс System.Object и его методы
- Обобщенные типы
- Ограничения обобщений
- Наследование обобщенных типов
- Конструкция try..catch..finally
- Блок catch и фильтры исключений
- Типы исключений. Класс Exception
- Генерация исключения и оператор throw
- Создание классов исключений
- Поиск блока catch при обработке исключений
- Делегаты
- Применение делегатов
- Анонимные методы
- Лямбды
- События
- Ковариантность и контравариантность делегатов
- Делегаты Action, Predicate и Func
- Замыкания
- Определение интерфейсов
- Применение интерфейсов
- Явная реализация интерфейсов
- Реализация интерфейсов в базовых и производных классах
- Наследование интерфейсов
- Интерфейсы в обобщениях
- Копирование объектов. Интерфейс ICloneable
- Сортировка объектов. Интерфейс IComparable
- Ковариантность и контравариантность обобщенных интерфейсов
- Определение операторов
- Перегрузка операций преобразования типов
- Индексаторы
- Переменные-ссылки и возвращение ссылки
- Методы расширения
- Частичные классы и методы
- Анонимные типы
- Кортежи
- Records
- Паттерн типов
- Паттерн свойств
- Паттерны кортежей
- Позиционный паттерн
- Реляционный и логический паттерны
- Паттерны списков
- Список List
- Двухсвязный список LinkedList
- Очередь Queue
- Стек Stack
- Словарь Dictionary
- Класс ObservableCollection
- Интерфейсы IEnumerable и IEnumerator
- Итераторы и оператор yield
- Класс Array и массивы
- Span
- Индексы и диапазоны
- Строки и класс System.String
- Операции со строками
- Форматирование и интерполяция строк
- Класс StringBuilder
- Регулярные выражения
- Структура DateTime
- Форматирование дат и времени
- DateOnly и TimeOnly
- Отложенная инициализация и тип Lazy
- Математические вычисления и класс Math
- Преобразование типов и класс Convert
- Введение в многопоточность. Класс Thread
- Создание потоков. Делегат ThreadStart
- Потоки с параметрами и ParameterizedThreadStart
- Синхронизация потоков
- Мониторы
- Класс AutoResetEvent
- Мьютексы
- Семафоры
- Задачи и класс Task
- Работа с классом Task
- Задачи продолжения
- Класс Parallel
- Отмена задач и параллельных операций. CancellationToken
- Асинхронные методы, async и await
- Возвращение результата из асинхронного метода
- Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny
- Обработка ошибок в асинхронных методах
- Асинхронные стримы
- Основы LINQ
- Проекция данных
- Фильтрация коллекции
- Сортировка
- Объединение, пересечение и разность коллекций
- Агрегатные операции
- Получение части коллекции
- Группировка
- Соединение коллекций
- Проверка наличия и получение элементов
- Отложенное и немедленное выполнение LINQ
- Делегаты в запросах LINQ
- Введение в Parallel LINQ. Метод AsParallel
- Метод AsOrdered
- Обработка ошибок и отмена параллельных операции
- Введение в рефлексию. Класс System.Type
- Применение рефлексии и исследование типов
- Исследование методов и конструкторов с помощью рефлексии
- Исследование полей и свойств с помощью рефлексии
- Динамическая загрузка сборок и позднее связывание
- Атрибуты в .NET
- DLR в C#. Ключевое слово dynamic
- DynamicObject и ExpandoObject
- Использование IronPython в .NET
- Сборщик мусора в C#
- Финализируемые объекты. Метод Dispose
- Конструкция using
- Указатели
- Указатели на структуры, члены классов и массивы
- Работа с дисками
- Работа с каталогами
- Работа с файлами. Классы File и FileInfo
- FileStream. Чтение и запись файла
- Чтение и запись текстовых файлов. StreamReader и StreamWriter
- Бинарные файлы. BinaryWriter и BinaryReader
- Архивация и сжатие файлов
- Сериализация в JSON. JsonSerializer
- XML-Документы
- Работа с XML с помощью System.Xml
- Изменение XML-документа
- XPath
- Linq to Xml. Создание Xml-документа
- Выборка элементов в LINQ to XML
- Изменение документа в LINQ to XML
- Сериализация в XML. XmlSerializer
- Процессы
- Домены приложений
- AssemblyLoadContext и динамическая загрузка и выгрузка сборок
- Native AOT
- Нововведения в C# 11
- Нововведения в C# 12
Помощь сайту
410011174743222
Перевод на карту
Номер карты:
4048415020898850Контакты для связи: metanit22@mail.ru
Copyright © metanit.com, 2023. Все права защищены.
Использование классов кодировки символов в .NET
В этой статье содержатся сведения об использовании классов, предоставляемых .NET, для кодирования и декодирования текста с помощью различных схем кодирования. В инструкциях предполагается, что вы прочитали Введения в кодировку символов в .NET.
Кодировщики и декодеры
.NET предоставляет классы кодировки для кодирования и декодирования текста с помощью различных систем кодирования. Например, класс UTF8Encoding описывает правила кодирования и декодирования UTF-8. Для экземпляров string .NET использует кодировку UTF-16 (представленную классом UnicodeEncoding). Кодировщики и декодеры доступны для других схем кодирования.
В процессе кодирования и декодирования также может выполняться проверка. Например, класс UnicodeEncoding проверяет все экземпляры char в диапазоне суррогатов, чтобы убедиться, что они находятся в допустимых суррогатных парах. Резервная стратегия определяет, как кодировщик обрабатывает недопустимые символы или как декодер обрабатывает недопустимые байты.
Классы кодировок .NET позволяют хранить и преобразовывать символьные данные. Их не следует использовать для хранения двоичных данных в строковом виде. В зависимости от используемой кодировки преобразование двоичных данных в строковый формат с использованием классов кодировок может привести к неожиданному результату и неточным или поврежденным данным. Для преобразования двоичных данных в строковый формат используйте метод Convert.ToBase64String .
Все классы кодировок в .NET наследуют от абстрактного класса System.Text.Encoding, который определяет общую для всех кодировок функциональность. Для доступа к отдельным объектам кодировок, реализованным в .NET, можно сделать следующее:
- Используйте статические свойства класса Encoding, которые возвращают объекты, представляющие стандартные кодировки символов .NET (ASCII, UTF-7, UTF-8, UTF-16 и UTF-32). Например, свойство Encoding.Unicode возвращает объект UnicodeEncoding . Каждый объект использует резервную стратегию замены для обработки строк, которые он не может закодировать, и байтов, которые не может декодировать. Дополнительные сведения см. в разделе Replacement fallback.
- Вызвать конструктор класса кодировки. Таким образом могут быть созданы объекты для кодировок ASCII, UTF-7, UTF-8, UTF-16 и UTF-32. По умолчанию каждый объект использует резервную стратегию замены для обработки строк, которые он не может закодировать, и байтов, которые он не может декодировать, но вы можете указать, чтобы вместо этого создавалось исключение. Дополнительные сведения см. в разделе Replacement fallback и Exception fallback.
- Вызвать конструктор Encoding(Int32) и передать ему целое число, представляющее кодировку. Объекты стандартных кодировок используют резервные стратегии замены, а объекты кодовых страниц и двухбайтовых кодировок (DBCS) используют резервную стратегию наилучшего соответствия для обработки строк, которые не удается закодировать, или байтов, которые не удается декодировать. Дополнительные сведения см. в разделе Best-fit fallback.
- Вызовите метод Encoding.GetEncoding, возвращающий любую стандартную кодировку, кодовую страницу или кодировку DBCS, доступную в .NET. Перегрузки позволяют задать резервный объект как для кодировщика, так и для декодера.
Информацию обо всех доступных в .NET кодировках можно получить, вызвав метод Encoding.GetEncodings. Платформа .NET поддерживает схемы кодировки символов, перечисленные в таблице ниже.
Класс кодировки Описание ASCII Кодирует ограниченный диапазон символов, используя семь младших битов байта. Так как эта кодировка поддерживает только символьные значения от U+0000 до U+007F , в большинстве случаев она не подходит для международных приложений. UTF-7 Представляет символы в виде последовательностей 7-разрядных символов ASCII. Символы Юникода, не относящиеся к ASCII, представляются в виде escape-последовательности символов ASCII. UTF-7 поддерживает протоколы, например электронной почты и группы новостей. Однако формат UTF-7 недостаточно безопасен и надежен. В некоторых случаях изменение одного бита может привести к существенному изменению интерпретации всей строки UTF-7. В других случаях для кодировки одного и того же текста могут использоваться разные строки UTF-7. В последовательностях, содержащих отличные от ASCII символы, формат UTF-7 требует больше места, чем UTF-8, а кодирование и декодирование выполняются медленнее. Поэтому по возможности лучше использовать UTF-8 вместо UTF-7. UTF-8 Представляет каждую кодовую точку Юникода в виде последовательности от одного до четырех байтов. UTF-8 поддерживает 8-разрядный размер данных и хорошо работает со многими операционными системами. Для диапазона символов ASCII кодировка UTF-8 идентична кодировке ASCII и предоставляет более широкий набор символов. Однако для китайской, японской и корейской письменности UTF-8 может потребовать три байта для каждого символа, что может привести к большему объему данных по сравнению с UTF-16. Иногда увеличение размера данных для китайской, японской и корейской письменности объясняется объемом данных ASCII, например тегами HTML. UTF-16 Представляет каждую кодовую точку Юникода в виде последовательности одного или двух 16-разрядных целых чисел. Наиболее распространенные символы Юникода требуют только одной кодовой точки UTF-16, хотя дополнительные символы Юникода (U+10000 и далее) требуют двух замещающих кодовых точек UTF-16. Поддерживаются оба порядка байтов: прямой и обратный. Кодировка UTF-16 используется средой CLR для представления значений Char и String , а операционной системой Windows — для представления значений WCHAR . UTF-32 Представляет каждую кодовую точку Юникода в виде 32-разрядного целого числа. Поддерживаются оба порядка байтов: прямой и обратный. Кодировка UTF-32 используется в случае, когда приложению требуется избежать поведения замещающей кодовой точки кодировки UTF-16 в операционных системах, в которых закодированное пространство имеет большое значение. Для кодирования отдельных отображаемых глифов может использоваться несколько символов UTF-32. Кодировка ANSI и ISO Предоставляет поддержку ряда кодовых страниц. В операционных системах Windows кодовые страницы используются для поддержки конкретного языка или группы языков. Таблицу кодовых страниц, поддерживаемых платформой .NET, можно найти в описании класса Encoding. Чтобы получить объект кодировки для конкретной кодовой страницы, можно вызвать метод Encoding.GetEncoding(Int32) . Кодовая страница содержит 256 кодовых точек с отсчетом от нуля. В большинстве кодовых страниц кодовые точки от 0 до 127 представляют набор символов ASCII, а кодовые точки от 128 до 255 существенно отличаются у разных кодовых страниц. Например, кодовая страница 1252 предоставляет символы для латинских систем письма, включая английский, немецкий и французский языки. Последние 128 кодовых точек на кодовой странице 1252 содержат диакритические знаки. Кодовая страница 1253 содержит коды символов, которые требуются в греческой системе письма. Последние 128 кодовых точек на кодовой странице 1253 содержат символы греческого языка. Таким образом, в приложении, использующем кодовые страницы ANSI, нельзя хранить греческий и немецкий тексты в одном потоке, если он не содержит идентификатор, указывающий соответствующую кодовую страницу. Двухбайтовые кодировки (DBCS) Поддерживают языки, такие как китайский, японский и корейский, содержащие более 256 символов. В кодировке DBCS каждый символ представлен парой кодовых точек (два байта). Свойство Encoding.IsSingleByte возвращает значение false для двухбайтовых кодировок. Чтобы получить объект кодировки для конкретной кодировки DBCS, можно вызвать метод Encoding.GetEncoding(Int32) . Когда приложение обрабатывает данные DBCS, первый байт символа DBCS (старший байт) обрабатывается в сочетании со вторым байтом, следующим непосредственно за ним. Так как одна пара двухбайтовых кодовых точек может представлять различные символы в зависимости от кодовой страницы, эта схема также не позволяет использовать в одном потоке данных два языка, например японский и китайский. Эти кодировки позволяют работать с символами Юникода, а также с кодировками, которые часто используются в приложениях прежних версий. Кроме того, можно создать настраиваемую кодировку, определив класс, производный от Encoding , и переопределив его члены.
Поддержка кодирования в .NET Core
По умолчанию .NET Core не предоставляет доступ к кодировкам кодовых страниц, кроме кодовой страницы 28591 и кодировок Юникода, например UTF-8 и UTF-16. Но вы можете добавить в свое приложение кодировки кодовых страниц из стандартных приложений Windows, ориентированных на .NET. Дополнительные сведения см. в разделе CodePagesEncodingProvider.
Выбор класса кодировки
Если у вас есть возможность выбрать кодировку для использования в приложении, следует использовать Юникод, предпочтительно UTF8Encoding или UnicodeEncoding. (Также .NET поддерживает третью кодировку Юникода: UTF32Encoding.)
Если вы планируете использовать кодировку ASCII (ASCIIEncoding), выберите вместо нее UTF8Encoding . Эти две кодировки идентичны для набора символов ASCII, но UTF8Encoding имеет указанные ниже преимущества.
- Она может представлять любой символ Юникода, тогда как ASCIIEncoding поддерживает только символы Юникода в диапазоне от U+0000 до U+007F.
- Она обеспечивает обнаружение ошибок и более высокий уровень безопасности.
- Она настроена для максимально быстрой работы и должна быть быстрее любых других кодировок. Даже для содержимого, имеющего только формат ASCII, выполнение операций с помощью UTF8Encoding происходит быстрее, чем с помощью ASCIIEncoding.
Кодировку ASCIIEncoding рекомендуется использовать только для приложений прежних версий. Однако даже для приложений прежних версий UTF8Encoding может быть предпочтительнее по указанным ниже причинам (при параметрах по умолчанию).
- Если содержимое приложения включает символы не только в формате ASCII, при кодировании с помощью ASCIIEncodingкаждый символ, не относящийся к ASCII, кодируется как знак вопроса (?). При последующем декодировании эти данные утрачиваются.
- Если содержимое приложения включает символы не только в формате ASCII, при кодировании с помощью UTF8Encodingпредставление символов в формате ASCII дает непригодный для чтения результат. Однако при последующем декодировании данных с помощью декодера UTF-8 обработка данных выполняется успешно.
В веб-приложении символы, отправленные клиенту в ответ на веб-запрос, должны отражать кодировку, используемую в клиенте. Как правило, требуется задать для свойства HttpResponse.ContentEncoding значение, возвращаемое свойством HttpRequest.ContentEncoding , для отображения текста в той кодировке, которую ожидает пользователь.
Использование объекта кодировки
Кодировщик преобразует строку символов (чаще всего символов Юникода) в их числовой (байтовый) эквивалент. Например, кодировщик ASCII можно использовать для преобразования символов Юникода в ASCII, чтобы они могли отображаться на консоли. Чтобы выполнить преобразование, вызовите метод Encoding.GetBytes . Если перед выполнением кодирования нужно определить, сколько байтов потребуется для хранения закодированных символов, можно вызвать метод GetByteCount .
В примере ниже один массив байтов используется для кодирования строк в двух отдельных операциях. Имеется индекс, указывающий начальную позицию в массиве байтов для следующего набора байтов, закодированных с использованием ASCII. Вызывается метод ASCIIEncoding.GetByteCount(String) для проверки того, что массив байтов достаточно велик для хранения закодированной строки. Затем вызывается метод ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) для кодирования символов в строке.
using System; using System.Text; public class Example < public static void Main() < string[] strings= < "This is the first sentence. ", "This is the second sentence. " >; Encoding asciiEncoding = Encoding.ASCII; // Create array of adequate size. byte[] bytes = new byte[49]; // Create index for current position of array. int index = 0; Console.WriteLine("Strings to encode:"); foreach (var stringValue in strings) < Console.WriteLine(" ", stringValue); int count = asciiEncoding.GetByteCount(stringValue); if (count + index >= bytes.Length) Array.Resize(ref bytes, bytes.Length + 50); int written = asciiEncoding.GetBytes(stringValue, 0, stringValue.Length, bytes, index); index = index + written; > Console.WriteLine("\nEncoded bytes:"); Console.WriteLine("", ShowByteValues(bytes, index)); Console.WriteLine(); // Decode Unicode byte array to a string. string newString = asciiEncoding.GetString(bytes, 0, index); Console.WriteLine("Decoded: ", newString); > private static string ShowByteValues(byte[] bytes, int last ) < string returnString = " "; for (int ctr = 0; ctr ", bytes[ctr]); > return returnString; > > // The example displays the following output: // Strings to encode: // This is the first sentence. // This is the second sentence. // // Encoded bytes: // // 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 // 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20 // 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20 // // Decoded: This is the first sentence. This is the second sentence.Imports System.Text Module Example Public Sub Main() Dim strings() As String = Dim asciiEncoding As Encoding = Encoding.ASCII ' Create array of adequate size. Dim bytes(50) As Byte ' Create index for current position of array. Dim index As Integer = 0 Console.WriteLine("Strings to encode:") For Each stringValue In strings Console.WriteLine(" ", stringValue) Dim count As Integer = asciiEncoding.GetByteCount(stringValue) If count + index >= bytes.Length Then Array.Resize(bytes, bytes.Length + 50) End If Dim written As Integer = asciiEncoding.GetBytes(stringValue, 0, stringValue.Length, bytes, index) index = index + written Next Console.WriteLine() Console.WriteLine("Encoded bytes:") Console.WriteLine("", ShowByteValues(bytes, index)) Console.WriteLine() ' Decode Unicode byte array to a string. Dim newString As String = asciiEncoding.GetString(bytes, 0, index) Console.WriteLine("Decoded: ", newString) End Sub Private Function ShowByteValues(bytes As Byte(), last As Integer) As String Dim returnString As String = " " For ctr As Integer = 0 To last - 1 If ctr Mod 20 = 0 Then returnString += vbCrLf + " " returnString += String.Format(" ", bytes(ctr)) Next Return returnString End Function End Module ' The example displays the following output: ' Strings to encode: ' This is the first sentence. ' This is the second sentence. ' ' Encoded bytes: ' ' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 ' 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20 ' 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20 ' ' Decoded: This is the first sentence. This is the second sentence.Декодер преобразует массив байтов, отражающий конкретную кодировку символов, в набор символов в массиве символов или в строке. Чтобы декодировать массив байтов в массив символов, вызовите метод Encoding.GetChars . Чтобы декодировать массив байтов в строку, вызовите метод GetString . Если перед декодированием нужно определить, сколько символов требуется для хранения раскодированных байтов, можно вызвать метод GetCharCount .
В примере ниже три строки кодируются, а затем декодируются в один массив символов. Имеется индекс, указывающий начальную позицию в массиве символов для следующего набора декодированных символов. Вызывается метод GetCharCount для проверки того, что массив символов достаточно велик для хранения всех декодированных символов. Затем вызывается метод ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) для декодирования массива байтов.
using System; using System.Text; public class Example < public static void Main() < string[] strings = < "This is the first sentence. ", "This is the second sentence. ", "This is the third sentence. " >; Encoding asciiEncoding = Encoding.ASCII; // Array to hold encoded bytes. byte[] bytes; // Array to hold decoded characters. char[] chars = new char[50]; // Create index for current position of character array. int index = 0; foreach (var stringValue in strings) < Console.WriteLine("String to Encode: ", stringValue); // Encode the string to a byte array. bytes = asciiEncoding.GetBytes(stringValue); // Display the encoded bytes. Console.Write("Encoded bytes: "); for (int ctr = 0; ctr < bytes.Length; ctr++) Console.Write(" ", ctr % 20 == 0 ? Environment.NewLine : "", bytes[ctr]); Console.WriteLine(); // Decode the bytes to a single character array. int count = asciiEncoding.GetCharCount(bytes); if (count + index >= chars.Length) Array.Resize(ref chars, chars.Length + 50); int written = asciiEncoding.GetChars(bytes, 0, bytes.Length, chars, index); index = index + written; Console.WriteLine(); > // Instantiate a single string containing the characters. string decodedString = new string(chars, 0, index - 1); Console.WriteLine("Decoded string: "); Console.WriteLine(decodedString); > > // The example displays the following output: // String to Encode: This is the first sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 // 6E 74 65 6E 63 65 2E 20 // // String to Encode: This is the second sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73 // 65 6E 74 65 6E 63 65 2E 20 // // String to Encode: This is the third sentence. // Encoded bytes: // 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65 // 6E 74 65 6E 63 65 2E 20 // // Decoded string: // This is the first sentence. This is the second sentence. This is the third sentence.Imports System.Text Module Example Public Sub Main() Dim strings() As String = Dim asciiEncoding As Encoding = Encoding.ASCII ' Array to hold encoded bytes. Dim bytes() As Byte ' Array to hold decoded characters. Dim chars(50) As Char ' Create index for current position of character array. Dim index As Integer For Each stringValue In strings Console.WriteLine("String to Encode: ", stringValue) ' Encode the string to a byte array. bytes = asciiEncoding.GetBytes(stringValue) ' Display the encoded bytes. Console.Write("Encoded bytes: ") For ctr As Integer = 0 To bytes.Length - 1 Console.Write(" ", If(ctr Mod 20 = 0, vbCrLf, ""), bytes(ctr)) Next Console.WriteLine() ' Decode the bytes to a single character array. Dim count As Integer = asciiEncoding.GetCharCount(bytes) If count + index >= chars.Length Then Array.Resize(chars, chars.Length + 50) End If Dim written As Integer = asciiEncoding.GetChars(bytes, 0, bytes.Length, chars, index) index = index + written Console.WriteLine() Next ' Instantiate a single string containing the characters. Dim decodedString As New String(chars, 0, index - 1) Console.WriteLine("Decoded string: ") Console.WriteLine(decodedString) End Sub End Module ' The example displays the following output: ' String to Encode: This is the first sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65 ' 6E 74 65 6E 63 65 2E 20 ' ' String to Encode: This is the second sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73 ' 65 6E 74 65 6E 63 65 2E 20 ' ' String to Encode: This is the third sentence. ' Encoded bytes: ' 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65 ' 6E 74 65 6E 63 65 2E 20 ' ' Decoded string: ' This is the first sentence. This is the second sentence. This is the third sentence.Методы кодирования и декодирования класса, производного от класса Encoding , предназначены для работы с полным набором данных. Это значит, что все данные, подлежащие кодированию или декодированию, предоставляются в одном вызове метода. Однако в некоторых случаях данные предоставляются в потоке, тогда данные для кодирования и декодирования можно получить только с помощью нескольких операций чтения. В таком случае необходимо, чтобы операция кодирования или декодирования "помнила" сохраненное после предыдущего вызова состояние. Методы классов, производных от Encoder и Decoder , могут обрабатывать операции кодирования и декодирования, охватывающие несколько вызовов методов.
Объект Encoder для конкретной кодировки доступен в ее свойстве Encoding.GetEncoder . Объект Decoder для конкретной кодировки доступен в ее свойстве Encoding.GetDecoder . Что касается операций декодирования, обратите внимание, что классы, производные от Decoder , включают метод Decoder.GetChars , но не имеют метода, соответствующего Encoding.GetString.
В примере ниже показано различие между использованием методов Encoding.GetString и Decoder.GetChars для декодирования массива байтов Юникода. В этом примере строка, содержащая несколько символов Юникода, кодируется в файл, а затем два метода декодирования используются для декодирования по десять байтов за раз. Так как замещающая пара оказывается в десятом и одиннадцатом байтах, она декодируется в отдельных вызовах метода. Как видно из выходных данных, метод Encoding.GetString не может правильно декодировать байты и заменяет их символом U+FFFD (замещающим символом). С другой стороны, метод Decoder.GetChars может успешно декодировать массив байтов для получения исходной строки.
using System; using System.IO; using System.Text; public class Example < public static void Main() < // Use default replacement fallback for invalid encoding. UnicodeEncoding enc = new UnicodeEncoding(true, false, false); // Define a string with various Unicode characters. string str1 = "AB YZ 19 \uD800\udc05 \u00e4"; str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308"; Console.WriteLine("Created original string. \n"); // Convert string to byte array. byte[] bytes = enc.GetBytes(str1); FileStream fs = File.Create(@".\characters.bin"); BinaryWriter bw = new BinaryWriter(fs); bw.Write(bytes); bw.Close(); // Read bytes from file. FileStream fsIn = File.OpenRead(@".\characters.bin"); BinaryReader br = new BinaryReader(fsIn); const int count = 10; // Number of bytes to read at a time. byte[] bytesRead = new byte[10]; // Buffer (byte array). int read; // Number of bytes actually read. string str2 = String.Empty; // Decoded string. // Try using Encoding object for all operations. do < read = br.Read(bytesRead, 0, count); str2 += enc.GetString(bytesRead, 0, read); >while (read == count); br.Close(); Console.WriteLine("Decoded string using UnicodeEncoding.GetString(). "); CompareForEquality(str1, str2); Console.WriteLine(); // Use Decoder for all operations. fsIn = File.OpenRead(@".\characters.bin"); br = new BinaryReader(fsIn); Decoder decoder = enc.GetDecoder(); char[] chars = new char[50]; int index = 0; // Next character to write in array. int written = 0; // Number of chars written to array. do < read = br.Read(bytesRead, 0, count); if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length) Array.Resize(ref chars, chars.Length + 50); written = decoder.GetChars(bytesRead, 0, read, chars, index); index += written; > while (read == count); br.Close(); // Instantiate a string with the decoded characters. string str3 = new String(chars, 0, index); Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString(). "); CompareForEquality(str1, str3); > private static void CompareForEquality(string original, string decoded) < bool result = original.Equals(decoded); Console.WriteLine("original = decoded: ", original.Equals(decoded, StringComparison.Ordinal)); if (! result) < Console.WriteLine("Code points in original string:"); foreach (var ch in original) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); Console.WriteLine("Code points in decoded string:"); foreach (var ch in decoded) Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > > // The example displays the following output: // Created original string. // // Decoded string using UnicodeEncoding.GetString(). // original = decoded: False // Code points in original string: // 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F // 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C // 0020 0073 0020 0062 0308 // Code points in decoded string: // 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F // 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C // 0020 0073 0020 0062 0308 // // Decoded string using UnicodeEncoding.Decoder.GetString(). // original = decoded: TrueImports System.IO Imports System.Text Module Example Public Sub Main() ' Use default replacement fallback for invalid encoding. Dim enc As New UnicodeEncoding(True, False, False) ' Define a string with various Unicode characters. Dim str1 As String = String.Format("AB YZ 19 ", ChrW(&hD800), ChrW(&hDC05), ChrW(&h00e4)) str1 += String.Format("Unicode characters. s ", ChrW(&h00a9), ChrW(&h010C), ChrW(&h0062), ChrW(&h0308)) Console.WriteLine("Created original string. ") Console.WriteLine() ' Convert string to byte array. Dim bytes() As Byte = enc.GetBytes(str1) Dim fs As FileStream = File.Create(".\characters.bin") Dim bw As New BinaryWriter(fs) bw.Write(bytes) bw.Close() ' Read bytes from file. Dim fsIn As FileStream = File.OpenRead(".\characters.bin") Dim br As New BinaryReader(fsIn) Const count As Integer = 10 ' Number of bytes to read at a time. Dim bytesRead(9) As Byte ' Buffer (byte array). Dim read As Integer ' Number of bytes actually read. Dim str2 As String = "" ' Decoded string. ' Try using Encoding object for all operations. Do read = br.Read(bytesRead, 0, count) str2 += enc.GetString(bytesRead, 0, read) Loop While read = count br.Close() Console.WriteLine("Decoded string using UnicodeEncoding.GetString(). ") CompareForEquality(str1, str2) Console.WriteLine() ' Use Decoder for all operations. fsIn = File.OpenRead(".\characters.bin") br = New BinaryReader(fsIn) Dim decoder As Decoder = enc.GetDecoder() Dim chars(50) As Char Dim index As Integer = 0 ' Next character to write in array. Dim written As Integer = 0 ' Number of chars written to array. Do read = br.Read(bytesRead, 0, count) If index + decoder.GetCharCount(bytesRead, 0, read) - 1 >= chars.Length Then Array.Resize(chars, chars.Length + 50) End If written = decoder.GetChars(bytesRead, 0, read, chars, index) index += written Loop While read = count br.Close() ' Instantiate a string with the decoded characters. Dim str3 As New String(chars, 0, index) Console.WriteLine("Decoded string using UnicodeEncoding.Decoder.GetString(). ") CompareForEquality(str1, str3) End Sub Private Sub CompareForEquality(original As String, decoded As String) Dim result As Boolean = original.Equals(decoded) Console.WriteLine("original = decoded: ", original.Equals(decoded, StringComparison.Ordinal)) If Not result Then Console.WriteLine("Code points in original string:") For Each ch In original Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Console.WriteLine("Code points in decoded string:") For Each ch In decoded Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If End Sub End Module ' The example displays the following output: ' Created original string. ' ' Decoded string using UnicodeEncoding.GetString(). ' original = decoded: False ' Code points in original string: ' 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055 006E 0069 0063 006F ' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C ' 0020 0073 0020 0062 0308 ' Code points in decoded string: ' 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055 006E 0069 0063 006F ' 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E 0020 00A9 0020 010C ' 0020 0073 0020 0062 0308 ' ' Decoded string using UnicodeEncoding.Decoder.GetString(). ' original = decoded: TrueВыбор резервной стратегии
Когда метод пытается закодировать или декодировать символ, но не находит сопоставления, он должен использовать резервную стратегию, определяющую, как должно обрабатываться отсутствие сопоставления. Существует три типа резервных стратегий:
- Best-Fit Fallback
- Replacement Fallback
- Exception Fallback
При операциях кодирования наиболее часто проблемы возникают, когда символ Юникода не удается сопоставить с определенной кодировкой кодовой страницы. При операциях декодирования наиболее часто проблемы возникают, когда недопустимую последовательность байтов не удается преобразовать в допустимые символы Юникода. Поэтому необходимо знать, какую резервную стратегию использует определенный объект кодировки. По возможности при создании экземпляра объекта следует указывать резервную стратегию, используемую объектом кодировки.
Best-Fit Fallback
Если символ не имеет точного соответствия в целевой кодировке, кодировщик может попытаться сопоставить его с похожим символом. (Наиболее подходящий вариант — это в основном проблема с кодировкой, а не с декодированием. Существует очень мало кодовых страниц, содержащих символы, которые не могут быть успешно сопоставлены с Юникодом.) Резервный вариант оптимально подходит по умолчанию для кодовых страниц и кодировок двухбайтовых наборов символов, которые извлекаются перегрузками Encoding.GetEncoding(Int32) и Encoding.GetEncoding(String) .
Теоретически все классы кодировок Юникода, доступные в .NET (UTF8Encoding, UnicodeEncoding и UTF32Encoding), поддерживают все символы всех наборов символов, поэтому при их использовании не возникает проблем со стратегией наилучшего соответствия.
Для разных кодовых страниц применяются разные стратегии наилучшего соответствия. Например, для некоторых кодовых страниц полноширинные латинские символы сопоставляются с более распространенными полуширинными символами. Для других кодовых страниц такое сопоставление не выполняется. Даже в случае применения активной стратегии наилучшего соответствия для некоторых символов некоторых кодировок отсутствует возможное сопоставление. Например, для идеографических символов китайского алфавита отсутствуют корректные сопоставления с символами кодовой страницы 1252. В этом случае используются замещающие строки. По умолчанию в качестве замещающей строки используется знак вопроса (U+003F).
Стратегии наилучшего соответствия не документированы подробно. Но для некоторых кодовых страниц их можно найти на веб-сайте Unicode Consortium. Изучите файл readme.txt в предоставленной папке, где описаны правила интерпретации для файлов сопоставления.
В примере ниже используется кодовая страница 1252 (кодовая страница Windows для западноевропейских языков) для иллюстрации стратегии наилучшего соответствия и ее недостатков. Метод Encoding.GetEncoding(Int32) используется для получения объекта кодировки для кодовой страницы 1252. По умолчанию для неподдерживаемых символов Юникода используется стратегия наилучшего соответствия. В примере создается экземпляр строки, содержащий три символа, не относящихся к ASCII (прописная латинская буква S в кружке (U+24C8), надстрочный индекс 5 (U+2075) и знак бесконечности (U+221E)), разделенные пробелами. Как видно из выходных данных примера, при кодировке строки три исходных отличных от пробела символа заменяются вопросительным знаком (U+003F), цифрой пять (U+0035) и цифрой восемь (U+0038). Цифра восемь — особенно неудачная замена неподдерживаемого знака бесконечности, а вопросительный знак показывает, что для исходного символа сопоставление не найдено.
using System; using System.Text; public class Example < public static void Main() < // Get an encoding for code page 1252 (Western Europe character set). Encoding cp1252 = Encoding.GetEncoding(1252); // Define and display a string. string str = "\u24c8 \u2075 \u221e"; Console.WriteLine("Original string: " + str); Console.Write("Code points in string: "); foreach (var ch in str) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine("\n"); // Encode a Unicode string. Byte[] bytes = cp1252.GetBytes(str); Console.Write("Encoded bytes: "); foreach (byte byt in bytes) Console.Write(" ", byt); Console.WriteLine("\n"); // Decode the string. string str2 = cp1252.GetString(bytes); Console.WriteLine("String round-tripped: ", str.Equals(str2)); if (! str.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); > > > // The example displays the following output: // Original string: Ⓢ ⁵ ∞ // Code points in string: 24C8 0020 2075 0020 221E // // Encoded bytes: 3F 20 35 20 38 // // String round-tripped: False // ? 5 8 // 003F 0020 0035 0020 0038Imports System.Text Module Example Public Sub Main() ' Get an encoding for code page 1252 (Western Europe character set). Dim cp1252 As Encoding = Encoding.GetEncoding(1252) ' Define and display a string. Dim str As String = String.Format(" ", ChrW(&h24c8), ChrW(&H2075), ChrW(&h221E)) Console.WriteLine("Original string: " + str) Console.Write("Code points in string: ") For Each ch In str Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Console.WriteLine() ' Encode a Unicode string. Dim bytes() As Byte = cp1252.GetBytes(str) Console.Write("Encoded bytes: ") For Each byt In bytes Console.Write(" ", byt) Next Console.WriteLine() Console.WriteLine() ' Decode the string. Dim str2 As String = cp1252.GetString(bytes) Console.WriteLine("String round-tripped: ", str.Equals(str2)) If Not str.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next End If End Sub End Module ' The example displays the following output: ' Original string: Ⓢ ⁵ ∞ ' Code points in string: 24C8 0020 2075 0020 221E ' ' Encoded bytes: 3F 20 35 20 38 ' ' String round-tripped: False ' ? 5 8 ' 003F 0020 0035 0020 0038По умолчанию для объекта Encoding применяется стратегия наилучшего соответствия, при которой данные в формате Юникод кодируются в формат кодовой страницы. Ряд приложений предыдущих версий построен с учетом этой стратегии. Однако в целях безопасности в большинстве новых приложений не рекомендуется применять эту стратегию. Например, приложениям не следует выполнять кодировку доменного имени в режиме наилучшего соответствия.
Вы также можете реализовать пользовательскую стратегию наилучшего соответствия для кодировки. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy .
Если стратегия наилучшего соответствия задана по умолчанию для объекта кодировки, вы можете выбрать другую резервную стратегию при извлечении объекта Encoding с помощью вызова перегрузки Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) или Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) . В следующем разделе приводится пример, в котором каждый символ, который не удается сопоставить с кодовой страницей 1252, заменяется звездочкой (*).
using System; using System.Text; public class Example < public static void Main() < Encoding cp1252r = Encoding.GetEncoding(1252, new EncoderReplacementFallback("*"), new DecoderReplacementFallback("*")); string str1 = "\u24C8 \u2075 \u221E"; Console.WriteLine(str1); foreach (var ch in str1) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); byte[] bytes = cp1252r.GetBytes(str1); string str2 = cp1252r.GetString(bytes); Console.WriteLine("Round-trip: ", str1.Equals(str2)); if (! str1.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > > // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // Round-trip: False // * * * // 002A 0020 002A 0020 002AImports System.Text Module Example Public Sub Main() Dim cp1252r As Encoding = Encoding.GetEncoding(1252, New EncoderReplacementFallback("*"), New DecoderReplacementFallback("*")) Dim str1 As String = String.Format(" ", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E)) Console.WriteLine(str1) For Each ch In str1 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Dim bytes() As Byte = cp1252r.GetBytes(str1) Dim str2 As String = cp1252r.GetString(bytes) Console.WriteLine("Round-trip: ", str1.Equals(str2)) If Not str1.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' Round-trip: False ' * * * ' 002A 0020 002A 0020 002AReplacement Fallback
Когда символ не имеет точного соответствия в целевой схеме и нет подходящего символа, с которым его можно сопоставить, приложение может использовать замещающий символ или строку. Так по умолчанию поступает декодер Юникода, заменяющий любую двухбайтовую последовательность, которую он не может декодировать, замещающим символом (U+FFFD). Кроме того, это поведение по умолчанию класса ASCIIEncoding , который заменяет каждый символ, который не удается кодировать или декодировать, вопросительным знаком. В примере ниже показана замена символов для строки Юникода из предыдущего примера. Как видно из выходных данных, каждый символ, который не удается декодировать в байтовое значение ASCII, заменяется 0x3F, то есть кодом ASCII для вопросительного знака.
using System; using System.Text; public class Example < public static void Main() < Encoding enc = Encoding.ASCII; string str1 = "\u24C8 \u2075 \u221E"; Console.WriteLine(str1); foreach (var ch in str1) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine("\n"); // Encode the original string using the ASCII encoder. byte[] bytes = enc.GetBytes(str1); Console.Write("Encoded bytes: "); foreach (var byt in bytes) Console.Write(" ", byt); Console.WriteLine("\n"); // Decode the ASCII bytes. string str2 = enc.GetString(bytes); Console.WriteLine("Round-trip: ", str1.Equals(str2)); if (! str1.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > > // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // // Encoded bytes: 3F 20 3F 20 3F // // Round-trip: False // ? ? ? // 003F 0020 003F 0020 003FImports System.Text Module Example Public Sub Main() Dim enc As Encoding = Encoding.Ascii Dim str1 As String = String.Format(" ", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E)) Console.WriteLine(str1) For Each ch In str1 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Console.WriteLine() ' Encode the original string using the ASCII encoder. Dim bytes() As Byte = enc.GetBytes(str1) Console.Write("Encoded bytes: ") For Each byt In bytes Console.Write(" ", byt) Next Console.WriteLine() Console.WriteLine() ' Decode the ASCII bytes. Dim str2 As String = enc.GetString(bytes) Console.WriteLine("Round-trip: ", str1.Equals(str2)) If Not str1.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' ' Encoded bytes: 3F 20 3F 20 3F ' ' Round-trip: False ' ? ? ? ' 003F 0020 003F 0020 003FВ .NET есть классы EncoderReplacementFallback и DecoderReplacementFallback, которые подставляют замещающую строку, если не удается точно сопоставить символ при кодировании или декодировании. По умолчанию эта замещающая строка — вопросительный знак, но вы можете вызвать перегрузку конструктора класса, чтобы выбрать другую строку. Как правило, замещающая строка — это отдельный символ, хотя это необязательно. В примере ниже поведение кодировщика кодовой страницы 1252 изменяется путем создания экземпляра объекта EncoderReplacementFallback , который использует символ звездочки (*) в качестве замещающей строки.
using System; using System.Text; public class Example < public static void Main() < Encoding cp1252r = Encoding.GetEncoding(1252, new EncoderReplacementFallback("*"), new DecoderReplacementFallback("*")); string str1 = "\u24C8 \u2075 \u221E"; Console.WriteLine(str1); foreach (var ch in str1) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); byte[] bytes = cp1252r.GetBytes(str1); string str2 = cp1252r.GetString(bytes); Console.WriteLine("Round-trip: ", str1.Equals(str2)); if (! str1.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > > // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // Round-trip: False // * * * // 002A 0020 002A 0020 002AImports System.Text Module Example Public Sub Main() Dim cp1252r As Encoding = Encoding.GetEncoding(1252, New EncoderReplacementFallback("*"), New DecoderReplacementFallback("*")) Dim str1 As String = String.Format(" ", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E)) Console.WriteLine(str1) For Each ch In str1 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Dim bytes() As Byte = cp1252r.GetBytes(str1) Dim str2 As String = cp1252r.GetString(bytes) Console.WriteLine("Round-trip: ", str1.Equals(str2)) If Not str1.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' Round-trip: False ' * * * ' 002A 0020 002A 0020 002AТакже можно реализовать класс замены для кодировки. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy .
Помимо вопросительного знака (U+003F) в качестве замещающей строки часто используется замещающий символ Юникода (U+FFFD), особенно при декодировании последовательностей байтов, которые не удается преобразовать в символы Юникода. Однако вы можете выбрать любую замещающую строку, в том числе из нескольких символов.
Exception Fallback
Вместо подстановки наиболее подходящей или замещающей строки кодировщик может создавать исключение EncoderFallbackException , если не удается закодировать набор символов, а декодер — создавать исключение DecoderFallbackException , если не удается декодировать массив байтов. Для создания исключения в операциях кодирования и декодирования методу EncoderExceptionFallback необходимо предоставить объект DecoderExceptionFallback или Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) соответственно. В примере ниже иллюстрируется резервная стратегия исключения с классом ASCIIEncoding .
using System; using System.Text; public class Example < public static void Main() < Encoding enc = Encoding.GetEncoding("us-ascii", new EncoderExceptionFallback(), new DecoderExceptionFallback()); string str1 = "\u24C8 \u2075 \u221E"; Console.WriteLine(str1); foreach (var ch in str1) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine("\n"); // Encode the original string using the ASCII encoder. byte[] bytes = <>; try < bytes = enc.GetBytes(str1); Console.Write("Encoded bytes: "); foreach (var byt in bytes) Console.Write("", byt); Console.WriteLine(); > catch (EncoderFallbackException e) < Console.Write("Exception: "); if (e.IsUnknownSurrogate()) Console.WriteLine("Unable to encode surrogate pair 0x0x at index .", Convert.ToUInt16(e.CharUnknownHigh), Convert.ToUInt16(e.CharUnknownLow), e.Index); else Console.WriteLine("Unable to encode 0x at index .", Convert.ToUInt16(e.CharUnknown), e.Index); return; > Console.WriteLine(); // Decode the ASCII bytes. try < string str2 = enc.GetString(bytes); Console.WriteLine("Round-trip: ", str1.Equals(str2)); if (! str1.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > catch (DecoderFallbackException e) < Console.Write("Unable to decode byte(s) "); foreach (byte unknown in e.BytesUnknown) Console.Write("0x"); Console.WriteLine("at index ", e.Index); > > > // The example displays the following output: // Ⓢ ⁵ ∞ // 24C8 0020 2075 0020 221E // // Exception: Unable to encode 0x24C8 at index 0.Imports System.Text Module Example Public Sub Main() Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New EncoderExceptionFallback(), New DecoderExceptionFallback()) Dim str1 As String = String.Format(" ", ChrW(&h24C8), ChrW(&h2075), ChrW(&h221E)) Console.WriteLine(str1) For Each ch In str1 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() Console.WriteLine() ' Encode the original string using the ASCII encoder. Dim bytes() As Byte = <> Try bytes = enc.GetBytes(str1) Console.Write("Encoded bytes: ") For Each byt In bytes Console.Write(" ", byt) Next Console.WriteLine() Catch e As EncoderFallbackException Console.Write("Exception: ") If e.IsUnknownSurrogate() Then Console.WriteLine("Unable to encode surrogate pair 0x 0x at index .", Convert.ToUInt16(e.CharUnknownHigh), Convert.ToUInt16(e.CharUnknownLow), e.Index) Else Console.WriteLine("Unable to encode 0x at index .", Convert.ToUInt16(e.CharUnknown), e.Index) End If Exit Sub End Try Console.WriteLine() ' Decode the ASCII bytes. Try Dim str2 As String = enc.GetString(bytes) Console.WriteLine("Round-trip: ", str1.Equals(str2)) If Not str1.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If Catch e As DecoderFallbackException Console.Write("Unable to decode byte(s) ") For Each unknown As Byte In e.BytesUnknown Console.Write("0x ") Next Console.WriteLine("at index ", e.Index) End Try End Sub End Module ' The example displays the following output: ' Ⓢ ⁵ ∞ ' 24C8 0020 2075 0020 221E ' ' Exception: Unable to encode 0x24C8 at index 0.Вы также можете реализовать пользовательский обработчик исключений для операции кодирования. Дополнительные сведения см. в разделе Implementing a Custom Fallback Strategy .
Объекты EncoderFallbackException и DecoderFallbackException предоставляют следующую информацию о состоянии, вызвавшем исключение:
- Объект EncoderFallbackException включает метод IsUnknownSurrogate , указывающий, представляют ли символы (или символ), которые не удается закодировать, неизвестную замещающую пару (тогда метод возвращает значение true ) или неизвестный отдельный символ (тогда метод возвращает значение false ). Символы замещающей пары доступны в свойствах EncoderFallbackException.CharUnknownHigh и EncoderFallbackException.CharUnknownLow . Неизвестный отдельный символ доступен в свойстве EncoderFallbackException.CharUnknown . Свойство EncoderFallbackException.Index указывает позицию первого символа, который не удалось закодировать, в строке.
- Объект DecoderFallbackException включает свойство BytesUnknown , возвращающее массив байтов, которые не удается декодировать. Свойство DecoderFallbackException.Index указывает начальную позицию неизвестных байтов.
Несмотря на то что объекты EncoderFallbackException и DecoderFallbackException предоставляют достаточно подробную диагностическую информацию об исключении, они не предоставляют доступ к буферу кодирования или декодирования. Поэтому они не позволяют заменять или исправлять недопустимые данные в методе кодирования или декодирования.
Implementing a Custom Fallback Strategy
Помимо встроенной стратегии наилучшего соответствия, реализованной кодовыми страницами, платформа .NET содержит следующие классы для реализации резервной стратегии:
- — классы EncoderReplacementFallback и EncoderReplacementFallbackBuffer можно использовать для замены символов в операциях кодирования;
- — классы DecoderReplacementFallback и DecoderReplacementFallbackBuffer можно использовать для замены символов в операциях декодирования;
- — классы EncoderExceptionFallback и EncoderExceptionFallbackBuffer можно использовать для создания исключения EncoderFallbackException , когда символ не удается закодировать;
- — классы DecoderExceptionFallback и DecoderExceptionFallbackBuffer можно использовать для создания исключения DecoderFallbackException , когда символ не удается декодировать.
Кроме того, можно реализовать пользовательское решение, использующее резервную стратегию наилучшего соответствия или стратегию исключения, выполнив указанные ниже действия.
- Создайте класс, производный от EncoderFallback , для операций кодирования и класс, производный от DecoderFallback , для операций декодирования.
- Создайте класс, производный от EncoderFallbackBuffer , для операций кодирования и класс, производный от DecoderFallbackBuffer , для операций декодирования.
- Для задания резервной стратегии исключения, если классы EncoderFallbackException и DecoderFallbackException не отвечают вашим требованиям, следует наследовать класс от объекта исключения, например Exception или ArgumentException.
Наследование от класса EncoderFallback или класса DecoderFallback
Для реализации пользовательской резервной стратегии необходимо создать класс, наследующий от EncoderFallback для операций кодирования и от класса DecoderFallback для операций декодирования. Экземпляры этих классов передаются в метод Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) и служат посредниками между классом кодировки и реализацией резервной стратегии.
При создании пользовательской резервной стратегии для кодировщика или декодера необходимо реализовать следующие члены:
- — свойство EncoderFallback.MaxCharCount или DecoderFallback.MaxCharCount , возвращающее максимально возможное число символов, которое может использоваться для замены одного символа в стратегиях наилучшего соответствия, замены или исключения. Для пользовательской резервной стратегии исключения его значение равно нулю.
- Метод EncoderFallback.CreateFallbackBuffer или DecoderFallback.CreateFallbackBuffer , возвращающий пользовательскую реализацию EncoderFallbackBuffer или DecoderFallbackBuffer . Метод вызывается кодировщиком, когда он встречает первый символ, который не удается закодировать, или декодером, когда он встречает первый байт, который не удается декодировать.
Наследование от класса EncoderFallbackBuffer или класса DecoderFallbackBuffer
Для реализации пользовательской резервной стратегии необходимо также создать класс, наследующий от EncoderFallbackBuffer для операций кодирования и от класса DecoderFallbackBuffer для операций декодирования. Экземпляры этих классов возвращаются методом CreateFallbackBufferEncoderFallback классов и DecoderFallback . Метод EncoderFallback.CreateFallbackBuffer вызывается кодировщиком, когда он встречает первый символ, который не удается закодировать, а метод DecoderFallback.CreateFallbackBuffer вызывается декодером, когда он встречает один или несколько байтов, которые не удается декодировать. Классы EncoderFallbackBuffer и DecoderFallbackBuffer предоставляют реализацию резервной стратегии. Каждый экземпляр представляет буфер, содержащий символы резервной стратегии, которые заменят символ, который не удалось закодировать, или последовательность байтов, которую не удалось декодировать.
При создании пользовательской резервной стратегии для кодировщика или декодера необходимо реализовать следующие члены:
- метод EncoderFallbackBuffer.Fallback или DecoderFallbackBuffer.Fallback . МетодEncoderFallbackBuffer.Fallback вызывается кодировщиком, чтобы предоставить резервный буфер со сведениями о символе, который не удается кодировать. Так как символ, который требуется закодировать, может быть замещающей парой, этот метод перегружается. Одной перегрузке передается символ, который нужно закодировать, и его индекс в строке. Второй перегрузке передаются верхний и нижний замещающий знаки и их индекс в строке. Метод DecoderFallbackBuffer.Fallback вызывается декодером, чтобы предоставить резервный буфер со сведениями о байтах, которые не удается декодировать. Этому методу передается массив байтов, которые не удалось декодировать, а также индекс первого байта. Метод резервной стратегии должен возвращать значение true , если резервный буфер может предоставить наилучшим образом соответствующий или замещающий символ (или символы); в противном случае он должен возвращать значение false . При использовании стратегии исключения метод резервной стратегии должен создавать исключение.
- Метод EncoderFallbackBuffer.GetNextChar или DecoderFallbackBuffer.GetNextChar , который вызывается кодировщиком или декодером многократно для получения следующего символа из резервного буфера. После возврата всех резервных символов метод должен вернуть символ U+0000.
- Свойство EncoderFallbackBuffer.Remaining или DecoderFallbackBuffer.Remaining , которое возвращает количество символов, оставшихся в резервном буфере.
- Метод EncoderFallbackBuffer.MovePrevious или DecoderFallbackBuffer.MovePrevious , который перемещает текущую позицию в резервном буфере к предыдущему символу.
- Метод EncoderFallbackBuffer.Reset или DecoderFallbackBuffer.Reset , повторно инициализирующий резервный буфер.
Если реализована резервная стратегия наилучшего соответствия или замены, классы, унаследованные от EncoderFallbackBuffer и DecoderFallbackBuffer , также имеют два закрытых поля экземпляра: точное число символов в буфере и индекс в буфере следующего символа, который нужно вернуть.
Пример EncoderFallback
В примере выше использовалась стратегия замены для замены символов Юникода, не соответствующих символам ASCII, звездочкой (*). В примере ниже используется пользовательская резервная стратегия наилучшего соответствия для получения более удачного сопоставления символов, отсутствующих в ASCII.
public class CustomMapper : EncoderFallback < public string DefaultString; internal Dictionarymapping; public CustomMapper() : this("*") < >public CustomMapper(string defaultString) < this.DefaultString = defaultString; // Create table of mappings mapping = new Dictionary(); mapping.Add(0x24C8, 0x53); mapping.Add(0x2075, 0x35); mapping.Add(0x221E, 0x49004E0046); > public override EncoderFallbackBuffer CreateFallbackBuffer() < return new CustomMapperFallbackBuffer(this); >public override int MaxCharCount < get < return 3; >> >Public Class CustomMapper : Inherits EncoderFallback Public DefaultString As String Friend mapping As Dictionary(Of UShort, ULong) Public Sub New() Me.New("?") End Sub Public Sub New(ByVal defaultString As String) Me.DefaultString = defaultString ' Create table of mappings mapping = New Dictionary(Of UShort, ULong) mapping.Add(&H24C8, &H53) mapping.Add(&H2075, &H35) mapping.Add(&H221E, &H49004E0046) End Sub Public Overrides Function CreateFallbackBuffer() As System.Text.EncoderFallbackBuffer Return New CustomMapperFallbackBuffer(Me) End Function Public Overrides ReadOnly Property MaxCharCount As Integer Get Return 3 End Get End Property End ClassВ примере кода ниже определяется класс CustomMapperFallbackBuffer , производный от EncoderFallbackBuffer. Словарь, содержащий сопоставления наилучшего соответствия и определенный в экземпляре класса CustomMapper , доступен из конструктора класса. Его метод Fallback возвращает значение true , если какие-либо символы Юникода, которые не удается кодировать кодировщику ASCII, определены в словаре сопоставлений; в противном случае возвращается значение false . Для каждого резервного действия закрытая переменная count указывает число символов, которые осталось вернуть, а закрытая переменная index указывает позицию в буфере строк (значение charsToReturn ) следующего символа, который нужно вернуть.
public class CustomMapperFallbackBuffer : EncoderFallbackBuffer < int count = -1; // Number of characters to return int index = -1; // Index of character to return CustomMapper fb; string charsToReturn; public CustomMapperFallbackBuffer(CustomMapper fallback) < this.fb = fallback; >public override bool Fallback(char charUnknownHigh, char charUnknownLow, int index) < // Do not try to map surrogates to ASCII. return false; >public override bool Fallback(char charUnknown, int index) < // Return false if there are already characters to map. if (count >= 1) return false; // Determine number of characters to return. charsToReturn = String.Empty; ushort key = Convert.ToUInt16(charUnknown); if (fb.mapping.ContainsKey(key)) < byte[] bytes = BitConverter.GetBytes(fb.mapping[key]); int ctr = 0; foreach (var byt in bytes) < if (byt >0) < ctr++; charsToReturn += (char) byt; >> count = ctr; > else < // Return default. charsToReturn = fb.DefaultString; count = 1; >this.index = charsToReturn.Length - 1; return true; > public override char GetNextChar() < // We'll return a character if possible, so subtract from the count of chars to return. count--; // If count is less than zero, we've returned all characters. if (count < 0) return '\u0000'; this.index--; return charsToReturn[this.index + 1]; >public override bool MovePrevious() < // Original: if count >= -1 and pos >= 0 if (count >= -1) < count++; return true; >else < return false; >> public override int Remaining < get < return count < 0 ? 0 : count; >> public override void Reset() < count = -1; index = -1; >>Public Class CustomMapperFallbackBuffer : Inherits EncoderFallbackBuffer Dim count As Integer = -1 ' Number of characters to return Dim index As Integer = -1 ' Index of character to return Dim fb As CustomMapper Dim charsToReturn As String Public Sub New(ByVal fallback As CustomMapper) MyBase.New() Me.fb = fallback End Sub Public Overloads Overrides Function Fallback(ByVal charUnknownHigh As Char, ByVal charUnknownLow As Char, ByVal index As Integer) As Boolean ' Do not try to map surrogates to ASCII. Return False End Function Public Overloads Overrides Function Fallback(ByVal charUnknown As Char, ByVal index As Integer) As Boolean ' Return false if there are already characters to map. If count >= 1 Then Return False ' Determine number of characters to return. charsToReturn = String.Empty Dim key As UShort = Convert.ToUInt16(charUnknown) If fb.mapping.ContainsKey(key) Then Dim bytes() As Byte = BitConverter.GetBytes(fb.mapping.Item(key)) Dim ctr As Integer For Each byt In bytes If byt > 0 Then ctr += 1 charsToReturn += Chr(byt) End If Next count = ctr Else ' Return default. charsToReturn = fb.DefaultString count = 1 End If Me.index = charsToReturn.Length - 1 Return True End Function Public Overrides Function GetNextChar() As Char ' We'll return a character if possible, so subtract from the count of chars to return. count -= 1 ' If count is less than zero, we've returned all characters. If count < 0 Then Return ChrW(0) Me.index -= 1 Return charsToReturn(Me.index + 1) End Function Public Overrides Function MovePrevious() As Boolean ' Original: if count >= -1 and pos >= 0 If count >= -1 Then count += 1 Return True Else Return False End If End Function Public Overrides ReadOnly Property Remaining As Integer Get Return If(count < 0, 0, count) End Get End Property Public Overrides Sub Reset() count = -1 index = -1 End Sub End ClassВ приведенном ниже коде создается экземпляр объекта CustomMapper , который передается в метод Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) . Выходные данные показывают, что реализация стратегии наилучшего соответствия успешно обрабатывает три символа исходной строки, отсутствующие в ASCII.
using System; using System.Collections.Generic; using System.Text; class Program < static void Main() < Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(), new DecoderExceptionFallback()); string str1 = "\u24C8 \u2075 \u221E"; Console.WriteLine(str1); for (int ctr = 0; ctr ", Convert.ToUInt16(str1[ctr]).ToString("X4")); if (ctr == str1.Length - 1) Console.WriteLine(); > Console.WriteLine(); // Encode the original string using the ASCII encoder. byte[] bytes = enc.GetBytes(str1); Console.Write("Encoded bytes: "); foreach (var byt in bytes) Console.Write(" ", byt); Console.WriteLine("\n"); // Decode the ASCII bytes. string str2 = enc.GetString(bytes); Console.WriteLine("Round-trip: ", str1.Equals(str2)); if (! str1.Equals(str2)) < Console.WriteLine(str2); foreach (var ch in str2) Console.Write("", Convert.ToUInt16(ch).ToString("X4")); Console.WriteLine(); > > >Imports System.Text Imports System.Collections.Generic Module Module1 Sub Main() Dim enc As Encoding = Encoding.GetEncoding("us-ascii", New CustomMapper(), New DecoderExceptionFallback()) Dim str1 As String = String.Format(" ", ChrW(&H24C8), ChrW(&H2075), ChrW(&H221E)) Console.WriteLine(str1) For ctr As Integer = 0 To str1.Length - 1 Console.Write(" ", Convert.ToUInt16(str1(ctr)).ToString("X4")) If ctr = str1.Length - 1 Then Console.WriteLine() Next Console.WriteLine() ' Encode the original string using the ASCII encoder. Dim bytes() As Byte = enc.GetBytes(str1) Console.Write("Encoded bytes: ") For Each byt In bytes Console.Write(" ", byt) Next Console.WriteLine() Console.WriteLine() ' Decode the ASCII bytes. Dim str2 As String = enc.GetString(bytes) Console.WriteLine("Round-trip: ", str1.Equals(str2)) If Not str1.Equals(str2) Then Console.WriteLine(str2) For Each ch In str2 Console.Write(" ", Convert.ToUInt16(ch).ToString("X4")) Next Console.WriteLine() End If End Sub End ModuleСм. также
- Общие сведения о кодировании символов в .NET
- Encoder
- Decoder
- DecoderFallback
- Encoding
- EncoderFallback
- Глобализация и локализация
