]>FreeBASIC, кракозябры, кодировки и кириллица

FreeBASIC, кракозябры, кодировки и кириллица

Аватар пользователя mabu

Самый часто задаваемый вопрос новичков по фрибейсику: как выводить кириллицу и русские буквы.

  1. Кодировки
  2. Правильный способ
  3. Неправильные способы

Кодировки

Настоящей проблемой при локализации всегда были операции с различными наборами символов. Годами, кодируя текстовые строки как последовательности однобайтовых символов с нулём в конце, большинство программистов так к этому привыкло, что это стало чуть ли не второй их натурой. Графическое начертание символа определялось его номером по специальной кодировочной таблице. Таким образом можно было представить 256 символов. Первые 127 символов у таких таблиц обычно совпадали, остальные менялись в зависимости языка и локализации. Таких таблиц было создано очень много. Во времена DOS для кириллицы корпорация Microsoft создала кодировку 866, с появлением Windows родилась кодировка 1251. Это значит, символ с номером больше 127 в разных кодировках выглядел по‐разному.

Всё изменилось, когда пришёл юникод. Юникод предложил единую таблицу соответствия графического начертания для всех символов в мире. Таблица юникода содержит очень много символов и для её представления в тексте также придумали специальные юникодные кодировки: UTF-8, UTF-16, UTF-32 и так далее.

Windows NT в лицах 2000, XP, Vista, 7, 8 и 10 — операционные системы целиком и полностью построенные на юникоде. Все базовые функции для создания окон, вывода текста, операций со строками и так далее ожидают передачи юникодных строк. Если какой‐то функции Windows передаётся ANSI‐строка, она сначала преобразуется в юникод и лишь потом передаётся операционной системе. Если ты ждёшь результата функции в виде ANSI‐строки, то операционная система преобразует строку перед возвратом в приложение из юникода в ANSI. Все эти операции протекают скрытно, но на них тратятся лишнее время и память.

Например, функция WriteConsole, вызываемая с ANSI‐строками, должна, выделив дополнительные блоки памяти (в стандартной куче процесса) преобразовать эти строки в юникод и, сохранив результат в выделенных блоках памяти, вызвать юникодную версию WriteConsole. Для функций, заполняющих строками выделенные буферы прежде чем программа сможет их обработать, системе нужно преобразовать строки из юникодных в ANSI. Из‐за этого твоё приложение потребует больше памяти и будет работать медленнее. Поэтому гораздо эффективнее разрабатывать программу с самого начала ориентируясь на юникод.

FreeBASIC как наследник Microsoft QuickBasic по умолчанию считает, что исходный текст написан в кодировке DOS. При выводе русского текста на консоль появляются кракозябры из‐за того, что один и тот же символ имеет разные коды в кодировках 866 и 1251. Эту проблему можно решить несколькими способами.

Правильный способ

Необходимо раз и навсегда отказаться от однобайтных кодировок и перейти на юникод. На дворе двадцать первый век, а люди до сих пор сидят в мире 1251 или 866.

Чтобы перейти на юникод, необходимо:

Если с первыми двумя условиями всё просто, то последний пункт может вызвать некоторые сложности, например, придётся самостоятельно выделять и очищать память для строк.

Строка WString — это не то же самое, что String с юникодом. Разница между ними представлена в таблице.

Тип String

Тип WString

Юникод

Может содержать юникод только в кодировке UTF-8.

Полностью юникодная.

Внутреннее представление

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

Статический массив символов, заканчивающийся нулевым символом; либо указатель на этот массив.

Размер одного символа

1 байт.

2 байта на Windows, суррогатные пары не учитываются.

Создание

Создание пустой строки, в строке содержится только нулевой символ:
Dim s As String

Создание строки вместе с инициализацией литералом:
Dim s As String = "Hello World"

Можно создать в виде строки фиксированной длины:
Dim s As WString * (количество символов + 1 на нулевой)

Либо в виде указателя на адрес в памяти:
Dim s As WString Ptr = Allocate((количество символов + 1 на нулевой) * SizeOf(WString))

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

Автоматическое вычисление длины строки при создании

Вычисляется автоматически.

Требуется вычислять самостоятельно.

Накладные расходы

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

При уничтожении строки должна освобождаться память из кучи.

Память для строк фиксированного размера выделяется статически, на этапе компиляции программы.

Статическая память не трубет очистки и освобождения.

Строки String просты в использовании и прекрасны. По сути этот тип скрывает в себе строку ZString вместе с её длиной, а компилятор берёт на себя всю грязную работу по выделению и освобождению памяти. Но они обманчивы и таят в себе ловушку однобайтной кодировки.

Также с консолью связано множество мифов, например:

Миф

Правда

Консоль и консольные программы — это DOS‐программы

Консоль — это полноценное Windows‐приложение. С точки зрения загрузчика программ, консольные приложения от графических отличаются всего лишь специальным значением в исполняемом файле.

Консоль существует в кодировке 866 или 1251

Консоль полностью поддерживает юникод. Данный миф связан с тем, что юникод не очень хорошо поддерживается в командных файлах (*.cmd и *.bat), которые приходится использовать в неюникодной кодировке.

Неправильные способы

К неправильным способам вывода кириллицы на консоль является попытки изменения кодовой страницы консоли, например, такими способами:

Код FreeBASIC
Exec("mode", "con cp select=866")

Также неправильно перекодировать строки из кодировки 866 в 1251 или наоборот с помощью функций типа CharToOem или AnsiToOem.

Ещё более неправильным способом вывода русских букв является попытка смены шрифта консоли, сохранение исходника в кодировке 866 или самостоятельное перекодирование символов.

Поделись ссылочкой в социальных сетях