Sign in Join
Wiki copytemporary

Unicode Support in Lazarus/ru: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Введение
22
33Здесь описывается поддержка Unicode в «программах» Lazarus (консольных или серверных, без графического интерфейса) и «приложениях» (GUI с использованием LCL) при использовании особенностей FPC 3.0+.
44
55Решение является кросс-платформенным и использует кодировку UTF-8, которая отличается от UTF-16 Delphi, но вы можете написать код, полностью совместимый с Delphi на уровне исходного кода, запомнив лишь несколько правил.
66
77Поддержка Unicode включается автоматически для приложений LCL начиная с Lazarus 1.6.0 при компиляции с FPC 3.0+.
88
99Старый метод поддержки UTF-8 в LCL при использовании FPC версий до 2.6.4 включительно, описан здесь: [[LCL Unicode Support]]
1010
1111## RTL с кодовой страницей UTF-8 по умолчанию
1212
1313По умолчанию RTL использует системную кодовую страницу для AnsiStrings (в таких операциях, как например FileExists и TStringList.LoadFromFile). Под Windows это не-Unicode кодировка, поэтому могут использоваться только символы из текущей языковой группы (не более 256 символов). LCL, с другой стороны, работает с кодировкой UTF-8, охватывающей весь диапазон Unicode. Под Linux и macOS UTF-8 обычно является системной кодовой страницей, и здесь RTL использует по умолчанию CP_UTF8.
1414
1515FPC, начиная с версии 3.0, обеспечивает API для изменения кодовой страницы RTL по умолчанию на другое значение. Lazarus (конкретно пакет LazUtils) использует данный API и изменяет кодовую страницу RTL по умолчанию на UTF-8 (CP_UTF8). Это означает, что пользователи Windows также могут теперь использовать строки UTF-8 в RTL.
1616
1717- Например, FileExists и StringList.LoadFromFile(Filename) теперь имеют полную поддержку Unicode. См. Полный перечень функций, которые полносттью поддерживают Unicode, здесь:
1818
1919[[FPC Unicode support|RTL changes]]
2020
2121- *AnsiToUTF8, UTF8ToAnsi, SysToUTF8, UTF8ToSys* не работают (не изменяют передаваемых данных). Эти функции обычно использовались для упомянутых выше функций RTL, которые больше не нуждаются в преобразованиях. Относящееся к функциям WinAPI, см ниже.
2222- Многочисленные вызовы *UTF8Encode* и *UTF8Decode* больше не требуются, потому что такие действия при присваивании значений *UnicodeString* переменным типа String и наоборот компилятор делает автоматически.
2323- При работе с WinAPI необходимо использовать "W"-функции или пользоваться функциями **UTF8ToWinCP** и **WinCPToUTF8**. То же верно для библиотек, которые до сих пор используют Ansi WinAPI функции. Например, в FPC 3.0 и более ранних версиях в этом нуждается unit registry.
2424- "String" и "UTF8String" — различные типы. Если вы присваиваете значение *String* переменной типа *UTF8String*, компилятор добавляет код для проверки совпадения кодировок. Это будет стоить дополнительного времени исполнения и увеличит размер кода. Просто используйте *String* вместо *UTF8String*.
2525- Консоль Windows использует кодировку, которая может отличаться от системной (в случае русского языка это всегда именно так). **writeln** в FPC 3.0+ автоматически преобразует строки UTF-8 в кодовую страницу консоли. Некоторые консольные программы Windows ожидают на входе строки в кодировке консоли и результаты своей работы также выдают в кодовой странице консоли. Для соответствующего преобразования можно использовать функции **UTF8ToConsole** и **ConsoleToUTF8**.
2626
2727Дополнительная информация о новинках поддержки Unicode в FPC: [[FPC Unicode support]]
2828
2929# Использование
3030
3131Следуйте простым правилам:
3232
3333- Используйте как обычно тип "String" вместо UTF8String или UnicodeString.
3434- Всегда присваивайте константу переменной типа String.
3535- Используйте тип UnicodeString явно для вызовов API, когда это необходимо.
3636
3737Эти правила делают большую часть кода уже совместимым с Delphi при использовании настроек проекта по умолчанию.
3838
3939## Применение в Lazarus
4040
4141Новый режим включается автоматически при компиляции FPC 3.0+. Это поведение может быть отключено директивой компиляции **-dDisableUTF8RTL**, детали см. на странице [[Lazarus with FPC3.0 without UTF-8 mode]].
4242
4343Если вы используете строковые литералы в новом режиме, ваши исходники всегда должны быть в кодировке UTF-8. Однако, ключ **-FcUTF8** на самом деле обычно не требуется. Дополнительные сведения приведены ниже, в разделе "Строковые литералы".
4444
4545Что же на самом деле происходит в новом режиме? В секции предварительной инициализации вызываются две FPC функции, устанавливающие кодировку строк по умолчанию в исполняющих библиотеках FPC в UTF-8 :
4646
47-` SetMultiByteConversionCodePage(CP_UTF8);`\
48-` SetMultiByteRTLFileSystemCodePage(CP_UTF8);`
47+```text
48+ SetMultiByteConversionCodePage(CP_UTF8);
49+ SetMultiByteRTLFileSystemCodePage(CP_UTF8);
50+```
4951
5052Кроме того, функции UTF8...() из LazUTF8 (LazUtils) устанавливаются в качестве функций обратного вызова для функций RTL, имена которых начинаются с Ansi...().
5153
5254## Использование UTF-8 в программах без LCL
5355
5456В не LCL-проекте добавьте зависимость для пакета **LazUtils**. Затем добавьте модуль **LazUTF8** в секцию uses основного файла программы. Он должен быть в начале, сразу после критических менеджеров памяти и многопоточности (например, cmem, heaptrc, cthreads).
5557
5658# Вызов функций API, которые используют WideString или UnicodeString
5759
5860Если тип параметра WideString или UnicodeString, вы можете просто передать ему строку. Компилятор преобразует данные автоматически. Появится предупреждение о преобразовании из AnsiString в UnicodeString, которое можно либо проигнорировать, либо подавить, приведя тип String к UnicodeString.
5961
6062Переменная S в приведенных ниже примерах определяется как String, что здесь означает AnsiString.
6163
6264``` pascal
6365procedure ApiCall(aParam: UnicodeString); // Определение
6466 ...
6567ApiCall(S); // вызов со строкой S, игнорируя предупреждение.
6668ApiCall(UnicodeString(S)); // вызов со строкой S, подавляя предупреждение (приведение String к UnicodeString).
6769```
6870
6971Когда тип параметра является указателем PWideChar, вам нужна временная переменная UnicodeString. Присвойте ей свою строку. Затем компилятор преобразует эти данные. Затем введите временную переменную в PWideChar.
7072
7173``` pascal
7274procedure ApiCallP(aParamP: PWideChar); // Определение
7375 ...
7476var Tmp: UnicodeString; // Временная переменная
7577 ...
7678Tmp := S; // Присваиваем String -> UnicodeString.
7779ApiCallP(PWideChar(Tmp)); // Вызов переменной temp, приведение к указателю
7880```
7981
8082> **Note:** в обоих случаях код совместим с Delphi. Это означает, что вы можете скопировать/вставить его в Delphi, и он сработает на 100% правильно. В Delphi String проецируется в UnicodeString.
8183
8284Типичным случаем является вызов Windows API. Только должны вызываться их версии "W", потому что они поддерживают Unicode. Обычно используйте UnicodeString также с Windows API. WideString необходим только при программировании COM/OLE, где ОС заботится об управлении памятью.
8385
8486# Чтение / запись текстового файла с кодовой страницей Windows
8587
8688Это не совместимо ни с Delphi, ни с прежним кодом Lazarus. На практике вы должны инкапсулировать код, связанный с системной кодовой страницей, и преобразовать данные в UTF-8 как можно быстрее.
8789
8890**Либо используйте RawByteString и сделайте явное преобразование...**
8991
9092``` pascal
9193uses ... , LConvEncoding;
9294 ...
9395var
9496 StrIn: RawByteString;
9597 StrOut: String;
9698 ...
9799StrOut := CP1252ToUTF8(StrIn,true); // Использует фиксированную кодовую страницу
98100// или
99101StrOut := WinCPToUTF8(StrIn,true); // Использует системную кодовую страницу на этом конкретном компьютере
100102```
101103
102104**... или установите правильную кодовую страницу для существующей строки**
103105
104106``` pascal
105107var
106108 StrIn: String;
107109 ...
108110SetCodePage(RawByteString(StrIn), 1252, false); // Фиксированная 1252 (или Windows.GetACP())
109111```
110112
111113> **Note:** в строковой переменной должен быть какой-то текст. Пустая строка на самом деле является указателем Nil, и вы не можете установить ее кодовую страницу.
112114
113115Windows.GetACP() возвращает системную кодовую страницу Windows.
114116
115117# Код, который очень сильно зависит от кодовой страницы Windows
116118
117119Существует «план B» для кода, который очень сильно зависит от системной кодовой страницы Windows или должен записываться в консоль Windows за пределами кодовой страницы консоли.\
118120См.: [[Lazarus with FPC3.0 without UTF-8 mode|Lazarus с FPC3.0 без режима UTF-8]].\
119121К счастью, это нужно нечасто. В большинстве случаев проще конвертировать данные в UTF-8.
120122
121123## Запись в консоль
122124
123125Вывод консоли Windows работает корректно, **если** ваши символы принадлежат кодовой странице консоли. Например, символ для рисования рамки `'╩'` в кодовой странице CP437 составляет один байт **\#202** и часто используется так:
124126
125127``` pascal
126128write('╩');
127129```
128130
129131Когда вы конвертируете код в UTF-8, например, используя пункт всплывающего меню редактора исходного кода Lazarus **File Settings (Параметры файла) / Encoding (Кодировка) / UTF-8**, и нажимая кнопку диалога "Change file on disk" (Изменить файл на диске), символ `'╩'` становится 3-х байтным (#226#149#169), поэтому литерал становится *строкой*.
130132
131133Процедуры *write* и *writeln* преобразуют строку UTF-8 в текущую кодовую страницу консоли. Таким образом, ваша консольная программа теперь выводит `'╩'` в Windows с любой кодовой страницей (т.е. не только с CP437), и она даже работает в Linux и macOS. Вы также можете использовать `'╩'` в строках LCL, например, Memo1.Lines.Add(`'╩'`);
132134
133135Если ваши символы **не** принадлежат кодовой странице консоли, все усложняется. Тогда вы вообще не сможете использовать новую систему Unicode.\
134136См.: [[Lazarus with FPC3.0 without UTF-8 mode|Проблема Системной кодировки и Консольной кодировки (Windows)]]
135137
136138# Символы Юникода и кодовые точки в коде
137139
138140См. детали для UTF-8 в [[UTF8 strings and characters/ru|UTF8 strings and characters]].
139141
140142## Функции кодовых точек для кодирования независимого кода
141143
142144В пакете LazUtils есть **модуль LazUnicode** со специальными функциями для работы с кодовыми точками, независимо от кодировки. Они используют функции UTF8...() из LazUTF8 при использовании в режиме UTF-8, и функции UTF16...() из LazUTF16 при использовании в {\$ModeSwitch UnicodeStrings} FPC или в Delphi (да, Delphi поддерживается!).
143145
144146В настоящее время {\$ModeSwitch UnicodeStrings} можно протестировать, задав "UseUTF16". Также есть тестовая программа LazUnicodeTest в каталоге components/lazutils/test. Она имеет 2 режима сборки, UTF8 и UTF16, для удобства тестирования. Тестовая программа также поддерживает Delphi, тогда, очевидно, используется режим UTF-16.
145147
146148LazUnicode позволяет одному исходному коду работать между:
147149
148150- Lazarus с его UTF-8 решением.
149151- Будущие FPC и Lazarus с Delphi-совместимым решением UTF-16.
150152- Delphi, где String = UnicodeString.
151153
152154Это обеспечивается перечисленными ниже функциями, не зависящим от кодирования:
153155
154156- `CodePointCopy()` - аналогична `UTF8Copy()`
155157- `CodePointLength()` - аналогична `UTF8Length()`
156158- `CodePointPos()` - аналогична `UTF8Pos()`
157159- `CodePointSize()` - аналогична `UTF8CharacterLength()` (функция устарела. Вместо нее используйте `UTF8CodepointSize`. [См. подробнее](https://lazarus-ccr.sourceforge.io/docs/lazutils/lazutf8/utf8characterlength.html))
158160- `UnicodeToWinCP()` - аналогична `UTF8ToWinCP()`
159161- `WinCPToUnicode()` - аналогична `WinCPToUTF8()`
160162
161163Этот режим также предоставляет перечислитель для кодовых точек, который компилятор использует для цикла for-in. В результате, независимо от кодировки, этот код работает:
162164
163165``` pascal
164166 var s, ch: String;
165167 ...
166168 for ch in s do
167169 writeln('ch=',ch);
168170```
169171
170172Delphi не предоставляет аналогичные функции для CodePoints в своем решении UTF-16. Практически большая часть кода Delphi рассматривает UTF-16 как кодировку с фиксированной шириной, что привело к возникновению большого количества неработающего кода UTF-16. Это означает, что использование LazUnicode также для Delphi улучшит качество кода!
171173
172174Для использования Delphi необходимы оба модуля LazUnicode и LazUTF16.
173175
174176# Строковые литералы
175177
176178Исходный код должен сохраняться в кодировке UTF-8. Lazarus создает такие файлы по умолчанию. Вы можете изменять кодировку импортированных файлов, щелкая правой кнопкой мыши в редакторе исходного кода / File Settings (Настройки файла) / Encoding (Кодировка).
177179
178180Обычно директива `{$codepage utf8} / -FcUTF8` не требуется. Это довольно нелогично, поскольку значение этого флага заключается в обработке строковых литералов как UTF-8. Однако новый режим UTF-8 переключает кодирование во время выполнения, а константы оцениваются во время компиляции.
179181
180182Таким образом, без `-FcUTF8` компилятор (ошибочно) считает, что строковая константа кодируется системной кодовой страницей. Затем он видит переменную String с кодировкой по умолчанию (которая будет изменена на UTF-8 во время выполнения, но компилятор этого не знает). Таким образом, то же для кодировки по умолчанию, преобразование не требуется, компилятор с радостью копирует символы, и все идет хорошо, в то время как на самом деле его дважды обманывали в течение процесса.
181183
182184**Пример:**
183185
184186По правилу шаловливых ручек, используйте тип «String» и заставляйте литералы работать.
185187
186188``` pascal
187189const s1 = 'äй';
188190const s2: string = #$C3#$A4; // ä
189191var s3;
190192...
191193 s3 := 'äöü';
192194```
193195
194196> **Note:** Строка UTF-8 может состоять из чисел, как это продемонстрировано для s2.
195197
196198- Литералы AnsiString/String работают как с директивой {\$codepage utf8} / -FcUTF8 , так и без неё.
197199
198200``` pascal
199201const s: string = 'äй';
200202```
201203
202204- Литералы ShortString работоспособны только **без** указания директивы {\$codepage utf8} / -FcUTF8. Вы можете сделать следующее:
203205
204206``` pascal
205207unit unit1;
206208{$Mode ObjFPC}{$H+}
207209{$modeswitch systemcodepage} // прекращает действие директивы -FcUTF8
208210interface
209211const s: string[15] = 'äй';
210212end.
211213```
212214
213215В качестве альтернативы, возможно использовать строки shortstring с включенной директивой \$codepage путем прямого присвоения кодов символов:
214216
215217``` pascal
216218unit unit1;
217219{$Mode ObjFPC}{$H+}
218220{$codepage utf8}
219221interface
220222const s: String[15] = #$C3#$A4; // ä
221223end.
222224```
223225
224226- WideString/UnicodeString/UTF8String работают только **с** {\$codepage utf8} / -FcUTF8.
225227
226228``` pascal
227229unit unit1;
228230{$Mode ObjFPC}{$H+}
229231{$codepage utf8}
230232interface
231233const ws: WideString = 'äй';
232234end.
233235```
234236
235237Присвоение строкового литерала другим строковым типам, кроме простого «String», более мудрено. Смотрите таблицы, что работает, а что нет.
236238
237239## Присвоение строковых литералов различным типам строк
238240
239241Здесь *работает* означает правильную кодовую страницу и правильные кодовые точки. Кодовая страница 0 или кодовая страница 65001 - обе являются правильными, они означают UTF-8.\
240242
241243### Без {\$codepage utf8} или переключателя компилятора -FcUTF8
242244
243245| Тип String, исходник в UTF-8 | Пример | Const (в исходнике) | Присвоение String | Присвоение UTF8String | Присвоение UnicodeString | Присвоение CP1252String | Присвоение RawByteString | Присвоение ShortString | Присвоение PChar |
244246|----|----|----|----|----|----|----|----|----|----|
245247| const | const s = 'äöü'; | working | working | wrong | wrong | wrong | working | working | working |
246248| String | const s: String = 'äöü'; | working | working | working | working | working | working | working | working |
247249| ShortString | const s: String\[15\] = 'äöü'; | working | working | working | working | wrong encoded | working | working | not available |
248250| UTF8String | const s: UTF8String = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
249251| UnicodeString | const s: UnicodeString = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
250252| String с объявленной кодовой страницей | type CP1252String = type AnsiString(1252); | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
251253| RawbyteString | const s: RawbyteString = 'äöü'; | working | working | working | working | to codepage 0 changed | working | working | working |
252254| PChar | const c: PChar = 'äöü'; | working | working | working | working | wrong | working | working | working |
253255
254256### С {\$codepage utf8} или переключателем компилятора -FcUTF8
255257
256258| Тип String, исходник в UTF-8 | Пример | Const (в исходнике) | Присвоение String | Присвоение UTF8String | Присвоение UnicodeString | Присвоение CP1252String | Присвоение RawByteString | Присвоение ShortString | Присвоение PChar |
257259|----|----|----|----|----|----|----|----|----|----|
258260| const | const s = 'äöü'; | UTF-16 encoded | working | working | working | working | working | working | working |
259261| String | const s: String = 'äöü'; | working | working | working | working | working | working | working | working |
260262| ShortString | const s: String\[15\] = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | not available |
261263| UTF8String | const s: UTF8String = 'äöü'; | working | working | working | working | working | working | working | working |
262264| UnicodeString | const s: UnicodeString = 'äöü'; | working | working | working | working | working | working | working | wrong |
263265| String с объявленной кодовой страницей | type CP1252String = type AnsiString(1252); | working | working | working | working | working | working | wrong | wrong |
264266| RawbyteString | const s: RawbyteString = 'äöü'; | working | working | working | working | to codepage 0 changed | working | working | working |
265267| PChar | const c: PChar = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
266268
267269Помните, что присваивание между переменными разных типов строк всегда работает благодаря их динамическому кодированию в FPC 3+. Данные конвертируются автоматически при необходимости.\
268270Только строковые литералы являются проблемой.
269271
270272# Переход из более старых версий Lazarus + LCL
271273
272274Ранее (до 1.6.0) LCL поддерживал Unicode с выделенными функциями UTF8. Код не был совместим с Delphi.
273275
274276Сейчас многие старые приложения LCL продолжают работать без изменений. Однако имеет смысл очистить код, чтобы сделать его более простым и более совместимым с Delphi. Код, который читает/записывает данные с использованием кодировки системной кодовой страницы Windows, нарушается и должен быть изменен. (См. [[Unicode Support in Lazarus/ru|Чтение / запись текстового файла с кодовой страницей Windows]]).
275277
276278Явные функции преобразования необходимы только для ввода-вывода с данными кодовой страницы Windows или при вызове функций Windows Ansi. В противном случае FPC позаботится о автоматическом преобразовании кодировок. Для преобразования вашего старого кода предусмотрены пустые функции преобразования.
277279
278280- UTF8Decode, UTF8Encode - Почти все можно удалить.
279281- UTF8ToAnsi, AnsiToUTF8 - Почти все можно удалить.
280282- UTF8ToSys, SysToUTF8 - Почти все можно удалить. Теперь они являются пустышками и возвращают только свои параметры.
281283
282284Файловые функции в RTL теперь заботятся о кодировке имен файлов. Все (?) связанные с именем файла функции ...UTF8() можно заменить на Delphi-совместимую функцию без суффикса UTF8. Например, FileExistsUTF8 можно заменить на FileExists.
283285
284286Большинство строковых функций UTF8...() можно заменить на совместимые с Delphi функции Ansi...(). Например, UTF8UpperCase() -\> AnsiUpperCase().
285287
286288Теперь Unicode работает и в программах без GUI. Требуется только зависимость от LazUtils и размещение модуля LazUTF8 в разделе uses основного файла программы.
287289
288290Для исторической справки, это была старая поддержка Unicode в LCL: [[Old LCL Unicode Support]]
289291
290292# Техническая реализация
291293
292294Что на самом деле происходит в системе Unicode? Эти 2 функции FPC вызываются в разделе ранней инициализации, устанавливая кодировку String по умолчанию в FPC в UTF-8:
293295
294-` SetMultiByteConversionCodePage(CP_UTF8);`\
295-` SetMultiByteRTLFileSystemCodePage(CP_UTF8);`
296+```text
297+ SetMultiByteConversionCodePage(CP_UTF8);
298+ SetMultiByteRTLFileSystemCodePage(CP_UTF8);
299+```
296300
297301В Windows функции UTF8...() в LazUTF8 (LazUtils) устанавливаются в качестве бэкэндов для строковых функций RTL Ansi...(). Таким образом, эти функции работают совместимым с Delphi способом.
298302
299303# Кодовые страницы FPC
300304
301305Компилятор (FPC) поддерживает указание кодовой страницы, которое может быть записано через опцию командной строки *-Fc* (т.е. *-Fcutf8*) или эквивалентную директиве *codepage* (т.е. *{\$codepage utf8}*). В этом случае, перед тем, как копировать байты, представляющие строковые константы в тексте вашей программы, компилятор будет интерпретировать все символьные данные в соответствии с указанной кодовой страницей. Есть две вещи, которые не стоит упускать из виду:
302306
303307- На платформах Unix, менеджер широких строк должен быть обязательно включен добавлением юнита *cwstring* в перечень uses. Без него программа не сможет правильно преобразовывать строковые данные во время исполнения.
304308
305309Менеджер широких строк добавляется по умолчанию в новом режиме UTF-8 RTL, однако это делает программу зависимой от libc и усложняет кросс-компиляцию.
306310
307311- Компилятор преобразует все строковые константы, содержащие символы, не относящиеся к ASCII, в константы типа widestring. Затем они автоматически преобразуются обратно в ansistring (либо во время компиляции, либо во время исполнения), но это может привести к искажениям, если вы попытаетесь смешать в одной строковой константе символы, напечатанные в тексте исходника и символы, заданные числовым представлением:
308312
309313Например:
310314
311315``` pascal
312316program project1;
313317{$codepage utf8}
314318{$mode objfpc}{$H+}
315319{$ifdef unix}
316320uses cwstring;
317321{$endif}
318322var
319323 a,b,c: string;
320324begin
321325 a:='ä';
322326 b:='='#$C3#$A4; // #$C3#$A4 - закодированный в UTF-8 символ ä
323327 c:='ä='#$C3#$A4; // после не относящегося к ascii 'ä' компилятор интерпретирует #$C3 в качестве отдельного widechar.
324328 writeln(a,b); // выводит ä=ä
325329 writeln(c); // выводит ä=ä
326330end.
327331```
328332
329333После компиляции и исполнения программа выведет:
330334
331335``` pascal
332336ä=ä
333337ä=ä
334338```
335339
336340Причина в том, что после того как в строке обнаружен символ *ä*, как упоминалось выше, оставшаяся часть строковой константы, присваиваемой переменной 'c', будет обработана как widestring. В результате \#\$C3 и \#\$A4 интерпретируются как widechar(#\$C3) и widechar(#\$A4), вместо прямой трансляции в байтовое представление.
337341
338342# Открытые вопросы
339343
340344- Символьные переменные в **TFormatSettings** (баг [27086](http://bugs.freepascal.org/view.php?id=27086)): например: **ThousandSeparator**, **DecimalSeparator**, **DateSeparator**, **TimeSeparator**, **ListSeparator**. Эти поля должны быть заменены на строки для корректной поддержки UTF-8. Например, под Linux с установками LC_NUMERIC=ru_RU.utf8 разделитель тысяч состоит из двух байт nbsp/160.
341345 - **Обход проблемы:** использовать только одиночные символы пробелов вместо того, что должно быть на самом деле, как это сделано в патче на баг [27099](http://bugs.freepascal.org/view.php?id=27099)
342346
343347### Вызовы функций WinAPI в библиотеках FPC
344348
345349- Unit registry, TRegistry - этот unit использует Ansi функции Windows API и поэтому вам придётся пользоваться UTF8ToWinCP, WinCPToUTF8. Раньше для этого требовался вызов UTF8ToSys.
346350- Все вызовы Windows функций с Ansi API в библиотеках FPC должны быть заменены версией W-API. Это в любом случае будет сделано для будущей поддержки UTF-16, поэтому никакого конфликта интересов нет. (прим. перев. - однако, эти действия означают декларированный отказ от дальнейшей поддержки версий Windows с неполной поддержкой Unicode - Windows 98, 95 и более ранних, а также некоторых мобильных и встроенных)
347351- **TProcess** - под Windows TProcess FPC 3.0 поддерживает только системную кодовую страницу. Необходимо либо использовать TProcessUTF8 из unit **utf8process** или патчить FPC, см. баг [29136](http://bugs.freepascal.org/view.php?id=29136)
348352
349353ToDo: Перечислить все относящиеся к багтрекеру FPC проблемы и патчи, которые могут эти проблемы решить.
350354
351355# Будущее
352356
353357Целью проекта FPC является создание решения, базирующегося на Delphi-совместимом UnicodeString (UTF-16), но пока мы к этому не готовы. Потребуется длительное время для такой реализации.
354358
355359Реализацию LCL на базе UTF-8 в её имеющемся виде необходимо рассматривать как временное решение. В будущем, когда в FPC будет полная поддержка UnicodeString как в RTL, так и в FCL, проект Lazarus обеспечит решения для LCL, использующее эти возможности. В то же время целью является и сохранение поддержки UTF-8, несмотря на то, что это может потребовать изменения в строковых типах или чего-то ещё. Деталей пока не знает никто. Мы обязательно сообщим вам о них, когда станет известно...
356360
357361В сущности, LCL скорее всего придётся в будущем разделиться на две версии - одну для UTF-8, и другую для UTF-16.
358362
359363# ЧаВо
360364
361365## Что насчет режима DelphiUnicode?
362366
363367**{\$Mode delphiunicode}** был добавлен в FPC 2.7.1 и похож на *{\$Mode Delphi}* с *{\$ModeSwitch UnicodeStrings}*. Смотрите следующий вопрос о *ModeSwitch UnicodeStrings*.
364368
365369## Как насчет ModeSwitch UnicodeStrings?
366370
367371**{\$ModeSwitch UnicodeStrings}** был добавлен в FPC 2.7.1 и определяет «String» как «UnicodeString» (UTF-16), «Char» как «WideChar», «PChar» как «PWideChar» и так далее. Это влияет только на текущий модуль. Другие модули, в том числе используемые этим модулем, имеют свое собственное определение «String». Многие строки и типы RTL (например, TStringList) используют 8-битные строки, которые требуют преобразования из/в UnicodeString, которые автоматически добавляются компилятором. LCL использует строки UTF-8. Рекомендуется использовать исходники в кодировке UTF-8 с или без "-FcUTF8".
368372
369373## Почему бы не использовать UTF8String в Lazarus?
370374
371375Кратко: потому что FCL не использует его.
372376
373377Исчерпывающе: **UTF8String** определен в модуле **system** как
374378
375379``` pascal
376380UTF8String = type AnsiString(CP_UTF8);
377381```
378382
379383Компилятор всегда предполагает, что он имеет кодировку UTF-8 (CP_UTF8), которая является многобайтовой кодировкой (то есть 1-4 байта на кодовую точку). Обратите внимание, что оператор \[\] обращается к байтам, а не к символам или кодовым точкам. То же самое для UnicodeString, но только слова вместо байтов. С другой стороны, предполагается, что **String** во время компиляции имеет *DefaultSystemCodePage* (CP_ACP). *DefaultSystemCodePage* определяется во время выполнения, поэтому компилятор консервативно полагает, что *String* и *UTF8String* имеют разные кодировки. Когда вы присваиваете или комбинируете *String* и *UTF8String*, компилятор вставляет код преобразования. То же самое для *ShortString* и *UTF8String*.
380384
381385Lazarus использует FCL, который использует *String*, поэтому использование *UTF8String* добавит конверсии. Если *DefaultSystemCodePage* не UTF-8, вы теряете символы. Если это UTF-8, то нет смысла использовать *UTF8String*.
382386
383387*UTF8String* станет полезным, когда в конце концов появится FCL UTF-16.
384388
385389## Почему UTF8String показывает странные символы, а String работает
386390
387391Например
388392
389393``` pascal
390394var s: UTF8String = 'ä';// с флагами по умолчанию (т.е. нет -Fc) это создает мусор
391395 // даже в системе UTF-8 Linux
392396```
393397
394398Вопрос: это ошибка? Ответ: Нет, потому что это работает как задокументировано.
395399
396400FPC игнорирует переменную LANG для создания в каждой системе одинакового результата. По историческим причинам/Delphi-совместимости он использует ISO-8859-1 по умолчанию. Lazarus предпочитает UTF-8.
397401
398402- Исходники UTF-8 работают со *String*, потому что
399403 1. FPC по умолчанию не добавляет код преобразования для обычных строковых литералов.
400404 2. *Исходная кодовая страница* равна *кодовой странице времени выполнения*. В Windows LazUTF8 устанавливает значение CP_UTF8.
401405- UTF8String требует исходников UTF-8. Начиная с FPC 3.0 для UTF8String вы должны сообщать компилятору, что исходник является UTF8 (*-FcUTF8*, *{\$codepage UTF8}* или сохранять файл как UTF-8 с BOM).
402406
403407> **Note:** Если вы сообщаете компилятору исходную кодировку UTF-8, он заменяет все строковые литералы ASCII этого модуля на UTF-16, увеличивая размер двоичного файла, добавляя некоторые служебные данные, и PChar для литералов требует явного преобразования. Вот почему Lazarus не добавляет его по умолчанию.
404408
405409## Что случится, если я использую {\$codepage utf8}?
406410
407411FPC имеет очень ограниченную поддержку UTF-8. Фактически, FPC поддерживает хранение литералов только как 8-битные строки с кодировкой «по умолчанию» или как widestrings (широкие строки). Поэтому любая кодовая страница не по умолчанию преобразуется в widestrings, даже если это системная кодовая страница. Например, большинство Linux/Mac/BSD используют UTF-8 в качестве системной кодовой страницы. Передача -Fcutf8 в компилятор сохранит строковый литерал как widestrings.
408412
409413Во время выполнения widestring литерал конвертируется. Когда вы присваиваете литерал AnsiString, widestring литерал преобразуется с помощью widestringmanager в кодировку системы. По умолчанию widestringmanager под Unix просто конвертирует widechars в символы, уничтожая любые символы не ASCII. Вы должны использовать widestringmanager, например, cwstring, чтобы получить правильное преобразование. Модуль LazUTF8 делает это.
410414
411415# См. также
412416
413417Информация о Unicode, кодовых страницах, строковых типах и RTL в FPC.\
414418Обратите внимание, что эта информация не совсем полезна для системы Lazarus UTF-8, потому что с точки зрения FPC она является хаком и меняет кодовую страницу по умолчанию.\
415419[[FPC Unicode support/ru|Поддержка Юникода в FPC]]
416420
417421---
418422*From the Free Pascal wiki, page "Unicode Support in Lazarus/ru", as it stood on 2020-03-02 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history