Sign in Join
Wiki copytemporary

Unicode Support in Lazarus/ru: what changed

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

OldNewLine
11# Введение
22
3-Здесь описывается поддержка Unicode в «программах» Lazarus (консольных или серверных, без графического интерфейса) и «приложениях» (GUI с использованием LCL) при использовании особенностей FPC 3.0+.
3+Здесь описывается поддержка Unicode в «программах» Lazarus (консольных или серверных, без графического интерфейса) и «приложениях» (GUI с использованием LCL) при использовании особенностей FPC 3.0+.
44
5-Решение является кросс-платформенным и использует кодировку UTF-8, которая отличается от UTF-16 Delphi, но вы можете написать код, полностью совместимый с Delphi на уровне исходного кода, запомнив лишь несколько правил.
5+Решение является кросс-платформенным и использует кодировку UTF-8, которая отличается от UTF-16 Delphi, но вы можете написать код, полностью совместимый с Delphi на уровне исходного кода, запомнив лишь несколько правил.
66
7-Поддержка Unicode включается автоматически для приложений LCL начиная с Lazarus 1.6.0 при компиляции с FPC 3.0+.
7+Поддержка 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
13-По умолчанию RTL использует системную кодовую страницу для AnsiStrings (в таких операциях, как например FileExists и TStringList.LoadFromFile). Под Windows это не-Unicode кодировка, поэтому могут использоваться только символы из текущей языковой группы (не более 256 символов). LCL, с другой стороны, работает с кодировкой UTF-8, охватывающей весь диапазон Unicode. Под Linux и macOS UTF-8 обычно является системной кодовой страницей, и здесь RTL использует по умолчанию CP_UTF8.
13+По умолчанию RTL использует системную кодовую страницу для AnsiStrings (в таких операциях, как например FileExists и TStringList.LoadFromFile). Под Windows это не-Unicode кодировка, поэтому могут использоваться только символы из текущей языковой группы (не более 256 символов). LCL, с другой стороны, работает с кодировкой UTF-8, охватывающей весь диапазон Unicode. Под Linux и macOS UTF-8 обычно является системной кодовой страницей, и здесь RTL использует по умолчанию CP_UTF8.
1414
15-FPC, начиная с версии 3.0, обеспечивает API для изменения кодовой страницы RTL по умолчанию на другое значение. Lazarus (конкретно пакет LazUtils) использует данный API и изменяет кодовую страницу RTL по умолчанию на UTF-8 (CP_UTF8). Это означает, что пользователи Windows также могут теперь использовать строки UTF-8 в RTL.
15+FPC, начиная с версии 3.0, обеспечивает API для изменения кодовой страницы RTL по умолчанию на другое значение. Lazarus (конкретно пакет LazUtils) использует данный API и изменяет кодовую страницу RTL по умолчанию на UTF-8 (CP_UTF8). Это означает, что пользователи Windows также могут теперь использовать строки UTF-8 в RTL.
1616
17-- Например, FileExists и StringList.LoadFromFile(Filename) теперь имеют полную поддержку Unicode. См. Полный перечень функций, которые полносттью поддерживают Unicode, здесь:
17+ * Например, FileExists и StringList.LoadFromFile(Filename) теперь имеют полную поддержку Unicode. См. Полный перечень функций, которые полносттью поддерживают Unicode, здесь:
1818
1919[[FPC Unicode support|RTL changes]]
2020
21-- *AnsiToUTF8, UTF8ToAnsi, SysToUTF8, UTF8ToSys* не работают (не изменяют передаваемых данных). Эти функции обычно использовались для упомянутых выше функций RTL, которые больше не нуждаются в преобразованиях. Относящееся к функциям WinAPI, см ниже.
22-- Многочисленные вызовы *UTF8Encode* и *UTF8Decode* больше не требуются, потому что такие действия при присваивании значений *UnicodeString* переменным типа String и наоборот компилятор делает автоматически.
23-- При работе с WinAPI необходимо использовать "W"-функции или пользоваться функциями **UTF8ToWinCP** и **WinCPToUTF8**. То же верно для библиотек, которые до сих пор используют Ansi WinAPI функции. Например, в FPC 3.0 и более ранних версиях в этом нуждается unit registry.
24-- "String" и "UTF8String" — различные типы. Если вы присваиваете значение *String* переменной типа *UTF8String*, компилятор добавляет код для проверки совпадения кодировок. Это будет стоить дополнительного времени исполнения и увеличит размер кода. Просто используйте *String* вместо *UTF8String*.
25-- Консоль Windows использует кодировку, которая может отличаться от системной (в случае русского языка это всегда именно так). **writeln** в FPC 3.0+ автоматически преобразует строки UTF-8 в кодовую страницу консоли. Некоторые консольные программы Windows ожидают на входе строки в кодировке консоли и результаты своей работы также выдают в кодовой странице консоли. Для соответствующего преобразования можно использовать функции **UTF8ToConsole** и **ConsoleToUTF8**.
21+ * _AnsiToUTF8, UTF8ToAnsi, SysToUTF8, UTF8ToSys_ не работают (не изменяют передаваемых данных). Эти функции обычно использовались для упомянутых выше функций RTL, которые больше не нуждаются в преобразованиях. Относящееся к функциям WinAPI, см ниже.
22+ * Многочисленные вызовы _UTF8Encode_ и _UTF8Decode_ больше не требуются, потому что такие действия при присваивании значений _UnicodeString_ переменным типа String и наоборот компилятор делает автоматически.
23+ * При работе с WinAPI необходимо использовать "W"-функции или пользоваться функциями **UTF8ToWinCP** и **WinCPToUTF8**. То же верно для библиотек, которые до сих пор используют Ansi WinAPI функции. Например, в FPC 3.0 и более ранних версиях в этом нуждается unit registry.
24+ * "String" и "UTF8String" — различные типы. Если вы присваиваете значение _String_ переменной типа _UTF8String_ , компилятор добавляет код для проверки совпадения кодировок. Это будет стоить дополнительного времени исполнения и увеличит размер кода. Просто используйте _String_ вместо _UTF8String_.
25+ * Консоль Windows использует кодировку, которая может отличаться от системной (в случае русского языка это всегда именно так). **writeln** в FPC 3.0+ автоматически преобразует строки UTF-8 в кодовую страницу консоли. Некоторые консольные программы Windows ожидают на входе строки в кодировке консоли и результаты своей работы также выдают в кодовой странице консоли. Для соответствующего преобразования можно использовать функции **UTF8ToConsole** и **ConsoleToUTF8**.
2626
27+
2728Дополнительная информация о новинках поддержки Unicode в FPC: [[FPC Unicode support]]
2829
2930# Использование
3031
31-Следуйте простым правилам:
32+Следуйте простым правилам:
3233
33-- Используйте как обычно тип "String" вместо UTF8String или UnicodeString.
34-- Всегда присваивайте константу переменной типа String.
35-- Используйте тип UnicodeString явно для вызовов API, когда это необходимо.
34+ * Используйте как обычно тип "String" вместо UTF8String или UnicodeString.
35+ * Всегда присваивайте константу переменной типа String.
36+ * Используйте тип UnicodeString явно для вызовов API, когда это необходимо.
3637
37-Эти правила делают большую часть кода уже совместимым с Delphi при использовании настроек проекта по умолчанию.
38+Эти правила делают большую часть кода уже совместимым с Delphi при использовании настроек проекта по умолчанию.
3839
3940## Применение в Lazarus
4041
41-Новый режим включается автоматически при компиляции FPC 3.0+. Это поведение может быть отключено директивой компиляции **-dDisableUTF8RTL**, детали см. на странице [[Lazarus with FPC3.0 without UTF-8 mode]].
42-
43-Если вы используете строковые литералы в новом режиме, ваши исходники всегда должны быть в кодировке UTF-8. Однако, ключ **-FcUTF8** на самом деле обычно не требуется. Дополнительные сведения приведены ниже, в разделе "Строковые литералы".
42+Новый режим включается автоматически при компиляции FPC 3.0+. Это поведение может быть отключено директивой компиляции **-dDisableUTF8RTL** , детали см. на странице [[Lazarus with FPC3.0 without UTF-8 mode]].
4443
45-Что же на самом деле происходит в новом режиме? В секции предварительной инициализации вызываются две FPC функции, устанавливающие кодировку строк по умолчанию в исполняющих библиотеках FPC в UTF-8 :
44+Если вы используете строковые литералы в новом режиме, ваши исходники всегда должны быть в кодировке UTF-8. Однако, ключ **-FcUTF8** на самом деле обычно не требуется. Дополнительные сведения приведены ниже, в разделе "Строковые литералы".
4645
47-```text
48- SetMultiByteConversionCodePage(CP_UTF8);
49- SetMultiByteRTLFileSystemCodePage(CP_UTF8);
46+Что же на самом деле происходит в новом режиме? В секции предварительной инициализации вызываются две FPC функции, устанавливающие кодировку строк по умолчанию в исполняющих библиотеках FPC в UTF-8 :
5047```
48+SetMultiByteConversionCodePage(CP_UTF8);
49+ SetMultiByteRTLFileSystemCodePage(CP_UTF8);
50+```
5151
52-Кроме того, функции UTF8...() из LazUTF8 (LazUtils) устанавливаются в качестве функций обратного вызова для функций RTL, имена которых начинаются с Ansi...().
52+Кроме того, функции UTF8...() из LazUTF8 (LazUtils) устанавливаются в качестве функций обратного вызова для функций RTL, имена которых начинаются с Ansi...().
5353
5454## Использование UTF-8 в программах без LCL
5555
56-В не LCL-проекте добавьте зависимость для пакета **LazUtils**. Затем добавьте модуль **LazUTF8** в секцию uses основного файла программы. Он должен быть в начале, сразу после критических менеджеров памяти и многопоточности (например, cmem, heaptrc, cthreads).
56+В не LCL-проекте добавьте зависимость для пакета **LazUtils**. Затем добавьте модуль **LazUTF8** в секцию uses основного файла программы. Он должен быть в начале, сразу после критических менеджеров памяти и многопоточности (например, cmem, heaptrc, cthreads). Если используется модуль **CRT** , он должен быть объявлен до **LazUTF8** , иначе вывод на консоль будет искажён.
5757
5858# Вызов функций API, которые используют WideString или UnicodeString
5959
60-Если тип параметра WideString или UnicodeString, вы можете просто передать ему строку. Компилятор преобразует данные автоматически. Появится предупреждение о преобразовании из AnsiString в UnicodeString, которое можно либо проигнорировать, либо подавить, приведя тип String к UnicodeString.
61-
62-Переменная S в приведенных ниже примерах определяется как String, что здесь означает AnsiString.
60+Если тип параметра WideString или UnicodeString, вы можете просто передать ему строку. Компилятор преобразует данные автоматически. Появится предупреждение о преобразовании из AnsiString в UnicodeString, которое можно либо проигнорировать, либо подавить, приведя тип String к UnicodeString.
6361
64-``` pascal
62+Переменная S в приведенных ниже примерах определяется как String, что здесь означает AnsiString.
63+```
6564procedure ApiCall(aParam: UnicodeString); // Определение
66- ...
67-ApiCall(S); // вызов со строкой S, игнорируя предупреждение.
68-ApiCall(UnicodeString(S)); // вызов со строкой S, подавляя предупреждение (приведение String к UnicodeString).
65+ ...
66+ ApiCall(S); // вызов со строкой S, игнорируя предупреждение.
67+ ApiCall(UnicodeString(S)); // вызов со строкой S, подавляя предупреждение (приведение String к UnicodeString).
6968```
7069
71-Когда тип параметра является указателем PWideChar, вам нужна временная переменная UnicodeString. Присвойте ей свою строку. Затем компилятор преобразует эти данные. Затем введите временную переменную в PWideChar.
72-
73-``` pascal
70+Когда тип параметра является указателем PWideChar, вам нужна временная переменная UnicodeString. Присвойте ей свою строку. Затем компилятор преобразует эти данные. Затем введите временную переменную в PWideChar.
71+```
7472procedure ApiCallP(aParamP: PWideChar); // Определение
75- ...
76-var Tmp: UnicodeString; // Временная переменная
77- ...
78-Tmp := S; // Присваиваем String -> UnicodeString.
79-ApiCallP(PWideChar(Tmp)); // Вызов переменной temp, приведение к указателю
73+ ...
74+ var Tmp: UnicodeString; // Временная переменная
75+ ...
76+ Tmp := S; // Присваиваем String -> UnicodeString.
77+ ApiCallP(PWideChar(Tmp)); // Вызов переменной temp, приведение к указателю
8078```
8179
82-> **Note:** в обоих случаях код совместим с Delphi. Это означает, что вы можете скопировать/вставить его в Delphi, и он сработает на 100% правильно. В Delphi String проецируется в UnicodeString.
80+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Примечание:** в обоих случаях код совместим с Delphi. Это означает, что вы можете скопировать/вставить его в Delphi, и он сработает на 100% правильно. В Delphi String проецируется в UnicodeString.
8381
84-Типичным случаем является вызов Windows API. Только должны вызываться их версии "W", потому что они поддерживают Unicode. Обычно используйте UnicodeString также с Windows API. WideString необходим только при программировании COM/OLE, где ОС заботится об управлении памятью.
82+Типичным случаем является вызов Windows API. Только должны вызываться их версии "W", потому что они поддерживают Unicode. Обычно используйте UnicodeString также с Windows API. WideString необходим только при программировании COM/OLE, где ОС заботится об управлении памятью.
8583
8684# Чтение / запись текстового файла с кодовой страницей Windows
8785
88-Это не совместимо ни с Delphi, ни с прежним кодом Lazarus. На практике вы должны инкапсулировать код, связанный с системной кодовой страницей, и преобразовать данные в UTF-8 как можно быстрее.
86+Это не совместимо ни с Delphi, ни с прежним кодом Lazarus. На практике вы должны инкапсулировать код, связанный с системной кодовой страницей, и преобразовать данные в UTF-8 как можно быстрее.
8987
9088**Либо используйте RawByteString и сделайте явное преобразование...**
91-
92-``` pascal
89+```
9390uses ... , LConvEncoding;
94- ...
95-var
96- StrIn: RawByteString;
97- StrOut: String;
98- ...
99-StrOut := CP1252ToUTF8(StrIn,true); // Использует фиксированную кодовую страницу
100-// или
101-StrOut := WinCPToUTF8(StrIn,true); // Использует системную кодовую страницу на этом конкретном компьютере
91+ ...
92+ var
93+ StrIn: RawByteString;
94+ StrOut: String;
95+ ...
96+ StrOut := CP1252ToUTF8(StrIn,true); // Использует фиксированную кодовую страницу
97+ // или
98+ StrOut := WinCPToUTF8(StrIn,true); // Использует системную кодовую страницу на этом конкретном компьютере
10299```
103100
104101**... или установите правильную кодовую страницу для существующей строки**
105-
106-``` pascal
102+```
107103var
108- StrIn: String;
109- ...
110-SetCodePage(RawByteString(StrIn), 1252, false); // Фиксированная 1252 (или Windows.GetACP())
104+ StrIn: String;
105+ ...
106+ SetCodePage(RawByteString(StrIn), 1252, false); // Фиксированная 1252 (или Windows.GetACP())
111107```
112108
113-> **Note:** в строковой переменной должен быть какой-то текст. Пустая строка на самом деле является указателем Nil, и вы не можете установить ее кодовую страницу.
109+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Примечание:** в строковой переменной должен быть какой-то текст. Пустая строка на самом деле является указателем Nil, и вы не можете установить ее кодовую страницу.
114110
115-Windows.GetACP() возвращает системную кодовую страницу Windows.
111+Windows.GetACP() возвращает системную кодовую страницу Windows.
116112
117113# Код, который очень сильно зависит от кодовой страницы Windows
118114
119-Существует «план B» для кода, который очень сильно зависит от системной кодовой страницы Windows или должен записываться в консоль Windows за пределами кодовой страницы консоли.\
120-См.: [[Lazarus with FPC3.0 without UTF-8 mode|Lazarus с FPC3.0 без режима UTF-8]].\
121-К счастью, это нужно нечасто. В большинстве случаев проще конвертировать данные в UTF-8.
115+Существует «план B» для кода, который очень сильно зависит от системной кодовой страницы Windows или должен записываться в консоль Windows за пределами кодовой страницы консоли.
116+См.: [[Lazarus with FPC3.0 without UTF-8 mode|Lazarus с FPC3.0 без режима UTF-8]].
117+К счастью, это нужно нечасто. В большинстве случаев проще конвертировать данные в UTF-8.
122118
123119## Запись в консоль
124120
125-Вывод консоли Windows работает корректно, **если** ваши символы принадлежат кодовой странице консоли. Например, символ для рисования рамки `'╩'` в кодовой странице CP437 составляет один байт **\#202** и часто используется так:
126-
127-``` pascal
121+Вывод консоли Windows работает корректно, **если** ваши символы принадлежат кодовой странице консоли. Например, символ для рисования рамки `'╩'` в кодовой странице CP437 составляет один байт **#202** и часто используется так:
122+```
128123write('╩');
129124```
130125
131-Когда вы конвертируете код в UTF-8, например, используя пункт всплывающего меню редактора исходного кода Lazarus **File Settings (Параметры файла) / Encoding (Кодировка) / UTF-8**, и нажимая кнопку диалога "Change file on disk" (Изменить файл на диске), символ `'╩'` становится 3-х байтным (#226#149#169), поэтому литерал становится *строкой*.
126+Когда вы конвертируете код в UTF-8, например, используя пункт всплывающего меню редактора исходного кода Lazarus **File Settings (Параметры файла) / Encoding (Кодировка) / UTF-8** , и нажимая кнопку диалога "Change file on disk" (Изменить файл на диске), символ `'╩'` становится 3-х байтным (#226#149#169), поэтому литерал становится _строкой_.
132127
133-Процедуры *write* и *writeln* преобразуют строку UTF-8 в текущую кодовую страницу консоли. Таким образом, ваша консольная программа теперь выводит `'╩'` в Windows с любой кодовой страницей (т.е. не только с CP437), и она даже работает в Linux и macOS. Вы также можете использовать `'╩'` в строках LCL, например, Memo1.Lines.Add(`'╩'`);
128+Процедуры _write_ и _writeln_ преобразуют строку UTF-8 в текущую кодовую страницу консоли. Таким образом, ваша консольная программа теперь выводит `'╩'` в Windows с любой кодовой страницей (т.е. не только с CP437), и она даже работает в Linux и macOS. Вы также можете использовать `'╩'` в строках LCL, например, Memo1.Lines.Add(`'╩'`);
134129
135-Если ваши символы **не** принадлежат кодовой странице консоли, все усложняется. Тогда вы вообще не сможете использовать новую систему Unicode.\
130+Если ваши символы **не** принадлежат кодовой странице консоли, все усложняется. Тогда вы вообще не сможете использовать новую систему Unicode.
136131См.: [[Lazarus with FPC3.0 without UTF-8 mode|Проблема Системной кодировки и Консольной кодировки (Windows)]]
137132
138133# Символы Юникода и кодовые точки в коде
139134
140-См. детали для UTF-8 в [[UTF8 strings and characters/ru|UTF8 strings and characters]].
135+См. детали для UTF-8 в [[UTF8 strings and characters/ru|UTF8 strings and characters]].
141136
142137## Функции кодовых точек для кодирования независимого кода
143138
144-В пакете LazUtils есть **модуль LazUnicode** со специальными функциями для работы с кодовыми точками, независимо от кодировки. Они используют функции UTF8...() из LazUTF8 при использовании в режиме UTF-8, и функции UTF16...() из LazUTF16 при использовании в {\$ModeSwitch UnicodeStrings} FPC или в Delphi (да, Delphi поддерживается!).
145-
146-В настоящее время {\$ModeSwitch UnicodeStrings} можно протестировать, задав "UseUTF16". Также есть тестовая программа LazUnicodeTest в каталоге components/lazutils/test. Она имеет 2 режима сборки, UTF8 и UTF16, для удобства тестирования. Тестовая программа также поддерживает Delphi, тогда, очевидно, используется режим UTF-16.
139+В пакете LazUtils есть **модуль LazUnicode** со специальными функциями для работы с кодовыми точками, независимо от кодировки. Они используют функции UTF8...() из LazUTF8 при использовании в режиме UTF-8, и функции UTF16...() из LazUTF16 при использовании в {$ModeSwitch UnicodeStrings} FPC или в Delphi (да, Delphi поддерживается!).
147140
148-LazUnicode позволяет одному исходному коду работать между:
141+В настоящее время {$ModeSwitch UnicodeStrings} можно протестировать, задав "UseUTF16". Также есть тестовая программа LazUnicodeTest в каталоге components/lazutils/test. Она имеет 2 режима сборки, UTF8 и UTF16, для удобства тестирования. Тестовая программа также поддерживает Delphi, тогда, очевидно, используется режим UTF-16.
149142
150-- Lazarus с его UTF-8 решением.
151-- Будущие FPC и Lazarus с Delphi-совместимым решением UTF-16.
152-- Delphi, где String = UnicodeString.
143+LazUnicode позволяет одному исходному коду работать между:
153144
154-Это обеспечивается перечисленными ниже функциями, не зависящим от кодирования:
145+ * Lazarus с его UTF-8 решением.
146+ * Будущие FPC и Lazarus с Delphi-совместимым решением UTF-16.
147+ * Delphi, где String = UnicodeString.
155148
156-- `CodePointCopy()` - аналогична `UTF8Copy()`
157-- `CodePointLength()` - аналогична `UTF8Length()`
158-- `CodePointPos()` - аналогична `UTF8Pos()`
159-- `CodePointSize()` - аналогична `UTF8CharacterLength()` (функция устарела. Вместо нее используйте `UTF8CodepointSize`. [См. подробнее](https://lazarus-ccr.sourceforge.io/docs/lazutils/lazutf8/utf8characterlength.html))
160-- `UnicodeToWinCP()` - аналогична `UTF8ToWinCP()`
161-- `WinCPToUnicode()` - аналогична `WinCPToUTF8()`
149+Это обеспечивается перечисленными ниже функциями, не зависящим от кодирования:
162150
163-Этот режим также предоставляет перечислитель для кодовых точек, который компилятор использует для цикла for-in. В результате, независимо от кодировки, этот код работает:
151+ * `CodePointCopy()` \- аналогична `UTF8Copy()`
152+ * `CodePointLength()` \- аналогична `UTF8Length()`
153+ * `CodePointPos()` \- аналогична `UTF8Pos()`
154+ * `CodePointSize()` \- аналогична `UTF8CharacterLength()` (функция **устарела**. Вместо нее используйте `UTF8CodepointSize`. [См. подробнее](<https://lazarus-ccr.sourceforge.io/docs/lazutils/lazutf8/utf8characterlength.html>))
155+ * `UnicodeToWinCP()` \- аналогична `UTF8ToWinCP()`
156+ * `WinCPToUnicode()` \- аналогична `WinCPToUTF8()`
164157
165-``` pascal
166- var s, ch: String;
167- ...
168- for ch in s do
169- writeln('ch=',ch);
158+Этот режим также предоставляет перечислитель для кодовых точек, который компилятор использует для цикла for-in. В результате, независимо от кодировки, этот код работает:
170159```
160+var s, ch: String;
161+ ...
162+ for ch in s do
163+ writeln('ch=',ch);
164+```
171165
172-Delphi не предоставляет аналогичные функции для CodePoints в своем решении UTF-16. Практически большая часть кода Delphi рассматривает UTF-16 как кодировку с фиксированной шириной, что привело к возникновению большого количества неработающего кода UTF-16. Это означает, что использование LazUnicode также для Delphi улучшит качество кода!
166+Delphi не предоставляет аналогичные функции для CodePoints в своем решении UTF-16. Практически большая часть кода Delphi рассматривает UTF-16 как кодировку с фиксированной шириной, что привело к возникновению большого количества неработающего кода UTF-16. Это означает, что использование LazUnicode также для Delphi улучшит качество кода!
173167
174-Для использования Delphi необходимы оба модуля LazUnicode и LazUTF16.
168+Для использования Delphi необходимы оба модуля LazUnicode и LazUTF16.
175169
176170# Строковые литералы
177171
178-Исходный код должен сохраняться в кодировке UTF-8. Lazarus создает такие файлы по умолчанию. Вы можете изменять кодировку импортированных файлов, щелкая правой кнопкой мыши в редакторе исходного кода / File Settings (Настройки файла) / Encoding (Кодировка).
172+Исходный код должен сохраняться в кодировке UTF-8. Lazarus создает такие файлы по умолчанию. Вы можете изменять кодировку импортированных файлов, щелкая правой кнопкой мыши в редакторе исходного кода / File Settings (Настройки файла) / Encoding (Кодировка).
179173
180-Обычно директива `{$codepage utf8} / -FcUTF8` не требуется. Это довольно нелогично, поскольку значение этого флага заключается в обработке строковых литералов как UTF-8. Однако новый режим UTF-8 переключает кодирование во время выполнения, а константы оцениваются во время компиляции.
174+Обычно директива `{$codepage utf8} / -FcUTF8` не требуется. Это довольно нелогично, поскольку значение этого флага заключается в обработке строковых литералов как UTF-8. Однако новый режим UTF-8 переключает кодирование во время выполнения, а константы оцениваются во время компиляции.
181175
182-Таким образом, без `-FcUTF8` компилятор (ошибочно) считает, что строковая константа кодируется системной кодовой страницей. Затем он видит переменную String с кодировкой по умолчанию (которая будет изменена на UTF-8 во время выполнения, но компилятор этого не знает). Таким образом, то же для кодировки по умолчанию, преобразование не требуется, компилятор с радостью копирует символы, и все идет хорошо, в то время как на самом деле его дважды обманывали в течение процесса.
176+Таким образом, без `-FcUTF8` компилятор (ошибочно) считает, что строковая константа кодируется системной кодовой страницей. Затем он видит переменную String с кодировкой по умолчанию (которая будет изменена на UTF-8 во время выполнения, но компилятор этого не знает). Таким образом, то же для кодировки по умолчанию, преобразование не требуется, компилятор с радостью копирует символы, и все идет хорошо, в то время как на самом деле его дважды обманывали в течение процесса.
183177
184178**Пример:**
185179
186-По правилу шаловливых ручек, используйте тип «String» и заставляйте литералы работать.
187-
188-``` pascal
180+По правилу шаловливых ручек, используйте тип «String» и заставляйте литералы работать.
181+```
189182const s1 = 'äй';
190-const s2: string = #$C3#$A4; // ä
191-var s3;
192-...
193- s3 := 'äöü';
183+ const s2: string = #$C3#$A4; // ä
184+ var s3;
185+ ...
186+ s3 := 'äöü';
194187```
195188
196-> **Note:** Строка UTF-8 может состоять из чисел, как это продемонстрировано для s2.
189+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Примечание:** Строка UTF-8 может состоять из чисел, как это продемонстрировано для s2.
197190
198-- Литералы AnsiString/String работают как с директивой {\$codepage utf8} / -FcUTF8 , так и без неё.
191+ * Литералы AnsiString/String работают как с директивой {$codepage utf8} / -FcUTF8 , так и без неё.
199192
200-``` pascal
193+```
201194const s: string = 'äй';
202195```
203196
204-- Литералы ShortString работоспособны только **без** указания директивы {\$codepage utf8} / -FcUTF8. Вы можете сделать следующее:
197+ * Литералы ShortString работоспособны только **без** указания директивы {$codepage utf8} / -FcUTF8. Вы можете сделать следующее:
205198
206-``` pascal
199+```
207200unit unit1;
208-{$Mode ObjFPC}{$H+}
209-{$modeswitch systemcodepage} // прекращает действие директивы -FcUTF8
210-interface
211-const s: string[15] = 'äй';
212-end.
201+ {$Mode ObjFPC}{$H+}
202+ {$modeswitch systemcodepage} // прекращает действие директивы -FcUTF8
203+ interface
204+ const s: string[15] = 'äй';
205+ end.
213206```
214207
215-В качестве альтернативы, возможно использовать строки shortstring с включенной директивой \$codepage путем прямого присвоения кодов символов:
216-
217-``` pascal
208+В качестве альтернативы, возможно использовать строки shortstring с включенной директивой $codepage путем прямого присвоения кодов символов:
209+```
218210unit unit1;
219-{$Mode ObjFPC}{$H+}
220-{$codepage utf8}
221-interface
222-const s: String[15] = #$C3#$A4; // ä
223-end.
211+ {$Mode ObjFPC}{$H+}
212+ {$codepage utf8}
213+ interface
214+ const s: String[15] = #$C3#$A4; // ä
215+ end.
224216```
225217
226-- WideString/UnicodeString/UTF8String работают только **с** {\$codepage utf8} / -FcUTF8.
218+ * WideString/UnicodeString/UTF8String работают только **с** {$codepage utf8} / -FcUTF8.
227219
228-``` pascal
220+```
229221unit unit1;
230-{$Mode ObjFPC}{$H+}
231-{$codepage utf8}
232-interface
233-const ws: WideString = 'äй';
234-end.
222+ {$Mode ObjFPC}{$H+}
223+ {$codepage utf8}
224+ interface
225+ const ws: WideString = 'äй';
226+ end.
235227```
236228
237-Присвоение строкового литерала другим строковым типам, кроме простого «String», более мудрено. Смотрите таблицы, что работает, а что нет.
229+Присвоение строкового литерала другим строковым типам, кроме простого «String», более мудрено. Смотрите таблицы, что работает, а что нет.
238230
239231## Присвоение строковых литералов различным типам строк
240232
241-Здесь *работает* означает правильную кодовую страницу и правильные кодовые точки. Кодовая страница 0 или кодовая страница 65001 - обе являются правильными, они означают UTF-8.\
233+Здесь _работает_ означает правильную кодовую страницу и правильные кодовые точки. Кодовая страница 0 или кодовая страница 65001 - обе являются правильными, они означают UTF-8.
242234
243-### Без {\$codepage utf8} или переключателя компилятора -FcUTF8
235+### Без {$codepage utf8} или переключателя компилятора -FcUTF8
244236
245237| Тип String, исходник в UTF-8 | Пример | Const (в исходнике) | Присвоение String | Присвоение UTF8String | Присвоение UnicodeString | Присвоение CP1252String | Присвоение RawByteString | Присвоение ShortString | Присвоение PChar |
246-|----|----|----|----|----|----|----|----|----|----|
238+|---|---|---|---|---|---|---|---|---|---|
247239| const | const s = 'äöü'; | working | working | wrong | wrong | wrong | working | working | working |
248240| String | const s: String = 'äöü'; | working | working | working | working | working | working | working | working |
249-| ShortString | const s: String\[15\] = 'äöü'; | working | working | working | working | wrong encoded | working | working | not available |
241+| ShortString | const s: String[15] = 'äöü'; | working | working | working | working | wrong encoded | working | working | not available |
250242| UTF8String | const s: UTF8String = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
251243| UnicodeString | const s: UnicodeString = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
252244| String с объявленной кодовой страницей | type CP1252String = type AnsiString(1252); | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
253245| RawbyteString | const s: RawbyteString = 'äöü'; | working | working | working | working | to codepage 0 changed | working | working | working |
254246| PChar | const c: PChar = 'äöü'; | working | working | working | working | wrong | working | working | working |
255247
256-### С {\$codepage utf8} или переключателем компилятора -FcUTF8
248+### С {$codepage utf8} или переключателем компилятора -FcUTF8
257249
258250| Тип String, исходник в UTF-8 | Пример | Const (в исходнике) | Присвоение String | Присвоение UTF8String | Присвоение UnicodeString | Присвоение CP1252String | Присвоение RawByteString | Присвоение ShortString | Присвоение PChar |
259-|----|----|----|----|----|----|----|----|----|----|
251+|---|---|---|---|---|---|---|---|---|---|
260252| const | const s = 'äöü'; | UTF-16 encoded | working | working | working | working | working | working | working |
261253| String | const s: String = 'äöü'; | working | working | working | working | working | working | working | working |
262-| ShortString | const s: String\[15\] = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | not available |
254+| ShortString | const s: String[15] = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | not available |
263255| UTF8String | const s: UTF8String = 'äöü'; | working | working | working | working | working | working | working | working |
264256| UnicodeString | const s: UnicodeString = 'äöü'; | working | working | working | working | working | working | working | wrong |
265257| String с объявленной кодовой страницей | type CP1252String = type AnsiString(1252); | working | working | working | working | working | working | wrong | wrong |
266258| RawbyteString | const s: RawbyteString = 'äöü'; | working | working | working | working | to codepage 0 changed | working | working | working |
267259| PChar | const c: PChar = 'äöü'; | wrong | wrong | wrong | wrong | wrong | wrong | wrong | wrong |
268260
269-Помните, что присваивание между переменными разных типов строк всегда работает благодаря их динамическому кодированию в FPC 3+. Данные конвертируются автоматически при необходимости.\
270-Только строковые литералы являются проблемой.
261+Помните, что присваивание между переменными разных типов строк всегда работает благодаря их динамическому кодированию в FPC 3+. Данные конвертируются автоматически при необходимости.
262+Только строковые литералы являются проблемой.
271263
272264# Переход из более старых версий Lazarus + LCL
273265
274-Ранее (до 1.6.0) LCL поддерживал Unicode с выделенными функциями UTF8. Код не был совместим с Delphi.
266+Ранее (до 1.6.0) LCL поддерживал Unicode с выделенными функциями UTF8. Код не был совместим с Delphi.
275267
276-Сейчас многие старые приложения LCL продолжают работать без изменений. Однако имеет смысл очистить код, чтобы сделать его более простым и более совместимым с Delphi. Код, который читает/записывает данные с использованием кодировки системной кодовой страницы Windows, нарушается и должен быть изменен. (См. [[Unicode Support in Lazarus/ru|Чтение / запись текстового файла с кодовой страницей Windows]]).
268+Сейчас многие старые приложения LCL продолжают работать без изменений. Однако имеет смысл очистить код, чтобы сделать его более простым и более совместимым с Delphi. Код, который читает/записывает данные с использованием кодировки системной кодовой страницы Windows, нарушается и должен быть изменен. (См. [[Unicode Support in Lazarus/ru|Чтение / запись текстового файла с кодовой страницей Windows]]).
277269
278-Явные функции преобразования необходимы только для ввода-вывода с данными кодовой страницы Windows или при вызове функций Windows Ansi. В противном случае FPC позаботится о автоматическом преобразовании кодировок. Для преобразования вашего старого кода предусмотрены пустые функции преобразования.
270+Явные функции преобразования необходимы только для ввода-вывода с данными кодовой страницы Windows или при вызове функций Windows Ansi. В противном случае FPC позаботится о автоматическом преобразовании кодировок. Для преобразования вашего старого кода предусмотрены пустые функции преобразования.
279271
280-- UTF8Decode, UTF8Encode - Почти все можно удалить.
281-- UTF8ToAnsi, AnsiToUTF8 - Почти все можно удалить.
282-- UTF8ToSys, SysToUTF8 - Почти все можно удалить. Теперь они являются пустышками и возвращают только свои параметры.
272+ * UTF8Decode, UTF8Encode - Почти все можно удалить.
273+ * UTF8ToAnsi, AnsiToUTF8 - Почти все можно удалить.
274+ * UTF8ToSys, SysToUTF8 - Почти все можно удалить. Теперь они являются пустышками и возвращают только свои параметры.
283275
284-Файловые функции в RTL теперь заботятся о кодировке имен файлов. Все (?) связанные с именем файла функции ...UTF8() можно заменить на Delphi-совместимую функцию без суффикса UTF8. Например, FileExistsUTF8 можно заменить на FileExists.
276+Файловые функции в RTL теперь заботятся о кодировке имен файлов. Все (?) связанные с именем файла функции ...UTF8() можно заменить на Delphi-совместимую функцию без суффикса UTF8. Например, FileExistsUTF8 можно заменить на FileExists.
285277
286-Большинство строковых функций UTF8...() можно заменить на совместимые с Delphi функции Ansi...(). Например, UTF8UpperCase() -\> AnsiUpperCase().
278+Большинство строковых функций UTF8...() можно заменить на совместимые с Delphi функции Ansi...(). Например, UTF8UpperCase() -> AnsiUpperCase().
287279
288-Теперь Unicode работает и в программах без GUI. Требуется только зависимость от LazUtils и размещение модуля LazUTF8 в разделе uses основного файла программы.
280+Теперь Unicode работает и в программах без GUI. Требуется только зависимость от LazUtils и размещение модуля LazUTF8 в разделе uses основного файла программы.
289281
290282Для исторической справки, это была старая поддержка Unicode в LCL: [[Old LCL Unicode Support]]
291283
292284# Техническая реализация
293285
294-Что на самом деле происходит в системе Unicode? Эти 2 функции FPC вызываются в разделе ранней инициализации, устанавливая кодировку String по умолчанию в FPC в UTF-8:
295-
296-```text
297- SetMultiByteConversionCodePage(CP_UTF8);
298- SetMultiByteRTLFileSystemCodePage(CP_UTF8);
286+Что на самом деле происходит в системе Unicode? Эти 2 функции FPC вызываются в разделе ранней инициализации, устанавливая кодировку String по умолчанию в FPC в UTF-8:
299287```
288+SetMultiByteConversionCodePage(CP_UTF8);
289+ SetMultiByteRTLFileSystemCodePage(CP_UTF8);
290+```
300291
301-В Windows функции UTF8...() в LazUTF8 (LazUtils) устанавливаются в качестве бэкэндов для строковых функций RTL Ansi...(). Таким образом, эти функции работают совместимым с Delphi способом.
292+В Windows функции UTF8...() в LazUTF8 (LazUtils) устанавливаются в качестве бэкэндов для строковых функций RTL Ansi...(). Таким образом, эти функции работают совместимым с Delphi способом.
302293
303294# Кодовые страницы FPC
304295
305-Компилятор (FPC) поддерживает указание кодовой страницы, которое может быть записано через опцию командной строки *-Fc* (т.е. *-Fcutf8*) или эквивалентную директиве *codepage* (т.е. *{\$codepage utf8}*). В этом случае, перед тем, как копировать байты, представляющие строковые константы в тексте вашей программы, компилятор будет интерпретировать все символьные данные в соответствии с указанной кодовой страницей. Есть две вещи, которые не стоит упускать из виду:
306-
307-- На платформах Unix, менеджер широких строк должен быть обязательно включен добавлением юнита *cwstring* в перечень uses. Без него программа не сможет правильно преобразовывать строковые данные во время исполнения.
296+Компилятор (FPC) поддерживает указание кодовой страницы, которое может быть записано через опцию командной строки _-Fc_ (т.е. _-Fcutf8_) или эквивалентную директиве _codepage_ (т.е. _{$codepage utf8}_). В этом случае, перед тем, как копировать байты, представляющие строковые константы в тексте вашей программы, компилятор будет интерпретировать все символьные данные в соответствии с указанной кодовой страницей. Есть две вещи, которые не стоит упускать из виду:
308297
309-Менеджер широких строк добавляется по умолчанию в новом режиме UTF-8 RTL, однако это делает программу зависимой от libc и усложняет кросс-компиляцию.
298+ * На платформах Unix, менеджер широких строк должен быть обязательно включен добавлением юнита _cwstring_ в перечень uses. Без него программа не сможет правильно преобразовывать строковые данные во время исполнения.
310299
311-- Компилятор преобразует все строковые константы, содержащие символы, не относящиеся к ASCII, в константы типа widestring. Затем они автоматически преобразуются обратно в ansistring (либо во время компиляции, либо во время исполнения), но это может привести к искажениям, если вы попытаетесь смешать в одной строковой константе символы, напечатанные в тексте исходника и символы, заданные числовым представлением:
300+Менеджер широких строк добавляется по умолчанию в новом режиме UTF-8 RTL, однако это делает программу зависимой от libc и усложняет кросс-компиляцию.
312301
313-Например:
302+ * Компилятор преобразует все строковые константы, содержащие символы, не относящиеся к ASCII, в константы типа widestring. Затем они автоматически преобразуются обратно в ansistring (либо во время компиляции, либо во время исполнения), но это может привести к искажениям, если вы попытаетесь смешать в одной строковой константе символы, напечатанные в тексте исходника и символы, заданные числовым представлением:
314303
315-``` pascal
304+Например:
305+```
316306program project1;
317-{$codepage utf8}
318-{$mode objfpc}{$H+}
319-{$ifdef unix}
320-uses cwstring;
321-{$endif}
322-var
323- a,b,c: string;
324-begin
325- a:='ä';
326- b:='='#$C3#$A4; // #$C3#$A4 - закодированный в UTF-8 символ ä
327- c:='ä='#$C3#$A4; // после не относящегося к ascii 'ä' компилятор интерпретирует #$C3 в качестве отдельного widechar.
328- writeln(a,b); // выводит ä=ä
329- writeln(c); // выводит ä=ä
330-end.
307+ {$codepage utf8}
308+ {$mode objfpc}{$H+}
309+ {$ifdef unix}
310+ uses cwstring;
311+ {$endif}
312+ var
313+ a,b,c: string;
314+ begin
315+ a:='ä';
316+ b:='='#$C3#$A4; // #$C3#$A4 - закодированный в UTF-8 символ ä
317+ c:='ä='#$C3#$A4; // после не относящегося к ascii 'ä' компилятор интерпретирует #$C3 в качестве отдельного widechar.
318+ writeln(a,b); // выводит ä=ä
319+ writeln(c); // выводит ä=ä
320+ end.
331321```
332322
333-После компиляции и исполнения программа выведет:
334-
335-``` pascal
323+После компиляции и исполнения программа выведет:
324+```
336325ä=ä
337-ä=ä
326+ ä=ä
338327```
339328
340-Причина в том, что после того как в строке обнаружен символ *ä*, как упоминалось выше, оставшаяся часть строковой константы, присваиваемой переменной 'c', будет обработана как widestring. В результате \#\$C3 и \#\$A4 интерпретируются как widechar(#\$C3) и widechar(#\$A4), вместо прямой трансляции в байтовое представление.
329+Причина в том, что после того как в строке обнаружен символ _ä_ , как упоминалось выше, оставшаяся часть строковой константы, присваиваемой переменной 'c', будет обработана как widestring. В результате #$C3 и #$A4 интерпретируются как widechar(#$C3) и widechar(#$A4), вместо прямой трансляции в байтовое представление.
341330
342331# Открытые вопросы
343332
344-- Символьные переменные в **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.
345- - **Обход проблемы:** использовать только одиночные символы пробелов вместо того, что должно быть на самом деле, как это сделано в патче на баг [27099](http://bugs.freepascal.org/view.php?id=27099)
333+ * Символьные переменные в **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.
334+ * **Обход проблемы:** использовать только одиночные символы пробелов вместо того, что должно быть на самом деле, как это сделано в патче на баг [27099](<http://bugs.freepascal.org/view.php?id=27099>)
346335
347336### Вызовы функций WinAPI в библиотеках FPC
348337
349-- Unit registry, TRegistry - этот unit использует Ansi функции Windows API и поэтому вам придётся пользоваться UTF8ToWinCP, WinCPToUTF8. Раньше для этого требовался вызов UTF8ToSys.
350-- Все вызовы Windows функций с Ansi API в библиотеках FPC должны быть заменены версией W-API. Это в любом случае будет сделано для будущей поддержки UTF-16, поэтому никакого конфликта интересов нет. (прим. перев. - однако, эти действия означают декларированный отказ от дальнейшей поддержки версий Windows с неполной поддержкой Unicode - Windows 98, 95 и более ранних, а также некоторых мобильных и встроенных)
351-- **TProcess** - под Windows TProcess FPC 3.0 поддерживает только системную кодовую страницу. Необходимо либо использовать TProcessUTF8 из unit **utf8process** или патчить FPC, см. баг [29136](http://bugs.freepascal.org/view.php?id=29136)
338+ * Unit registry, TRegistry - этот unit использует Ansi функции Windows API и поэтому вам придётся пользоваться UTF8ToWinCP, WinCPToUTF8. Раньше для этого требовался вызов UTF8ToSys.
339+ * Все вызовы Windows функций с Ansi API в библиотеках FPC должны быть заменены версией W-API. Это в любом случае будет сделано для будущей поддержки UTF-16, поэтому никакого конфликта интересов нет. (прим. перев. - однако, эти действия означают декларированный отказ от дальнейшей поддержки версий Windows с неполной поддержкой Unicode - Windows 98, 95 и более ранних, а также некоторых мобильных и встроенных)
340+ * **TProcess** \- под Windows TProcess FPC 3.0 поддерживает только системную кодовую страницу. Необходимо либо использовать TProcessUTF8 из unit **utf8process** или патчить FPC, см. баг [29136](<http://bugs.freepascal.org/view.php?id=29136>)
352341
353-ToDo: Перечислить все относящиеся к багтрекеру FPC проблемы и патчи, которые могут эти проблемы решить.
342+ToDo: Перечислить все относящиеся к багтрекеру FPC проблемы и патчи, которые могут эти проблемы решить.
354343
355344# Будущее
356345
357-Целью проекта FPC является создание решения, базирующегося на Delphi-совместимом UnicodeString (UTF-16), но пока мы к этому не готовы. Потребуется длительное время для такой реализации.
346+Целью проекта FPC является создание решения, базирующегося на Delphi-совместимом UnicodeString (UTF-16), но пока мы к этому не готовы. Потребуется длительное время для такой реализации.
358347
359-Реализацию LCL на базе UTF-8 в её имеющемся виде необходимо рассматривать как временное решение. В будущем, когда в FPC будет полная поддержка UnicodeString как в RTL, так и в FCL, проект Lazarus обеспечит решения для LCL, использующее эти возможности. В то же время целью является и сохранение поддержки UTF-8, несмотря на то, что это может потребовать изменения в строковых типах или чего-то ещё. Деталей пока не знает никто. Мы обязательно сообщим вам о них, когда станет известно...
348+Реализацию LCL на базе UTF-8 в её имеющемся виде необходимо рассматривать как временное решение. В будущем, когда в FPC будет полная поддержка UnicodeString как в RTL, так и в FCL, проект Lazarus обеспечит решения для LCL, использующее эти возможности. В то же время целью является и сохранение поддержки UTF-8, несмотря на то, что это может потребовать изменения в строковых типах или чего-то ещё. Деталей пока не знает никто. Мы обязательно сообщим вам о них, когда станет известно...
360349
361-В сущности, LCL скорее всего придётся в будущем разделиться на две версии - одну для UTF-8, и другую для UTF-16.
350+В сущности, LCL скорее всего придётся в будущем разделиться на две версии - одну для UTF-8, и другую для UTF-16.
362351
363352# ЧаВо
364353
365354## Что насчет режима DelphiUnicode?
366355
367-**{\$Mode delphiunicode}** был добавлен в FPC 2.7.1 и похож на *{\$Mode Delphi}* с *{\$ModeSwitch UnicodeStrings}*. Смотрите следующий вопрос о *ModeSwitch UnicodeStrings*.
356+**{$Mode delphiunicode}** был добавлен в FPC 2.7.1 и похож на _{$Mode Delphi}_ с _{$ModeSwitch UnicodeStrings}_. Смотрите следующий вопрос о _ModeSwitch UnicodeStrings_.
368357
369358## Как насчет ModeSwitch UnicodeStrings?
370359
371-**{\$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".
360+**{$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".
372361
373362## Почему бы не использовать UTF8String в Lazarus?
374363
375-Кратко: потому что FCL не использует его.
376-
377-Исчерпывающе: **UTF8String** определен в модуле **system** как
364+Кратко: потому что FCL не использует его.
378365
379-``` pascal
366+Исчерпывающе: **UTF8String** определен в модуле **system** как
367+```
380368UTF8String = type AnsiString(CP_UTF8);
381369```
382370
383-Компилятор всегда предполагает, что он имеет кодировку UTF-8 (CP_UTF8), которая является многобайтовой кодировкой (то есть 1-4 байта на кодовую точку). Обратите внимание, что оператор \[\] обращается к байтам, а не к символам или кодовым точкам. То же самое для UnicodeString, но только слова вместо байтов. С другой стороны, предполагается, что **String** во время компиляции имеет *DefaultSystemCodePage* (CP_ACP). *DefaultSystemCodePage* определяется во время выполнения, поэтому компилятор консервативно полагает, что *String* и *UTF8String* имеют разные кодировки. Когда вы присваиваете или комбинируете *String* и *UTF8String*, компилятор вставляет код преобразования. То же самое для *ShortString* и *UTF8String*.
371+Компилятор всегда предполагает, что он имеет кодировку UTF-8 (CP_UTF8), которая является многобайтовой кодировкой (то есть 1-4 байта на кодовую точку). Обратите внимание, что оператор [] обращается к байтам, а не к символам или кодовым точкам. То же самое для UnicodeString, но только слова вместо байтов. С другой стороны, предполагается, что **String** во время компиляции имеет _DefaultSystemCodePage_ (CP_ACP). _DefaultSystemCodePage_ определяется во время выполнения, поэтому компилятор консервативно полагает, что _String_ и _UTF8String_ имеют разные кодировки. Когда вы присваиваете или комбинируете _String_ и _UTF8String_ , компилятор вставляет код преобразования. То же самое для _ShortString_ и _UTF8String_.
384372
385-Lazarus использует FCL, который использует *String*, поэтому использование *UTF8String* добавит конверсии. Если *DefaultSystemCodePage* не UTF-8, вы теряете символы. Если это UTF-8, то нет смысла использовать *UTF8String*.
373+Lazarus использует FCL, который использует _String_ , поэтому использование _UTF8String_ добавит конверсии. Если _DefaultSystemCodePage_ не UTF-8, вы теряете символы. Если это UTF-8, то нет смысла использовать _UTF8String_.
386374
387-*UTF8String* станет полезным, когда в конце концов появится FCL UTF-16.
375+_UTF8String_ станет полезным, когда в конце концов появится FCL UTF-16.
388376
389377## Почему UTF8String показывает странные символы, а String работает
390378
391-Например
392-
393-``` pascal
379+Например
380+```
394381var s: UTF8String = 'ä';// с флагами по умолчанию (т.е. нет -Fc) это создает мусор
395- // даже в системе UTF-8 Linux
382+ // даже в системе UTF-8 Linux
396383```
397384
398-Вопрос: это ошибка? Ответ: Нет, потому что это работает как задокументировано.
385+Вопрос: это ошибка? Ответ: Нет, потому что это работает как задокументировано.
399386
400-FPC игнорирует переменную LANG для создания в каждой системе одинакового результата. По историческим причинам/Delphi-совместимости он использует ISO-8859-1 по умолчанию. Lazarus предпочитает UTF-8.
387+FPC игнорирует переменную LANG для создания в каждой системе одинакового результата. По историческим причинам/Delphi-совместимости он использует ISO-8859-1 по умолчанию. Lazarus предпочитает UTF-8.
401388
402-- Исходники UTF-8 работают со *String*, потому что
403- 1. FPC по умолчанию не добавляет код преобразования для обычных строковых литералов.
404- 2. *Исходная кодовая страница* равна *кодовой странице времени выполнения*. В Windows LazUTF8 устанавливает значение CP_UTF8.
405-- UTF8String требует исходников UTF-8. Начиная с FPC 3.0 для UTF8String вы должны сообщать компилятору, что исходник является UTF8 (*-FcUTF8*, *{\$codepage UTF8}* или сохранять файл как UTF-8 с BOM).
389+ * Исходники UTF-8 работают со _String_ , потому что
390+ 1. FPC по умолчанию не добавляет код преобразования для обычных строковых литералов.
391+ 2. _Исходная кодовая страница_ равна _кодовой странице времени выполнения_. В Windows LazUTF8 устанавливает значение CP_UTF8.
392+ * UTF8String требует исходников UTF-8. Начиная с FPC 3.0 для UTF8String вы должны сообщать компилятору, что исходник является UTF8 (_-FcUTF8_ , _{$codepage UTF8}_ или сохранять файл как UTF-8 с BOM).
406393
407-> **Note:** Если вы сообщаете компилятору исходную кодировку UTF-8, он заменяет все строковые литералы ASCII этого модуля на UTF-16, увеличивая размер двоичного файла, добавляя некоторые служебные данные, и PChar для литералов требует явного преобразования. Вот почему Lazarus не добавляет его по умолчанию.
394+![Light bulb](https://upload.wikimedia.org/wikipedia/commons/d/d8/Nuvola_apps_ktip.png) **Примечание:** Если вы сообщаете компилятору исходную кодировку UTF-8, он заменяет все строковые литералы ASCII этого модуля на UTF-16, увеличивая размер двоичного файла, добавляя некоторые служебные данные, и PChar для литералов требует явного преобразования. Вот почему Lazarus не добавляет его по умолчанию.
408395
409-## Что случится, если я использую {\$codepage utf8}?
396+## Что случится, если я использую {$codepage utf8}?
410397
411-FPC имеет очень ограниченную поддержку UTF-8. Фактически, FPC поддерживает хранение литералов только как 8-битные строки с кодировкой «по умолчанию» или как widestrings (широкие строки). Поэтому любая кодовая страница не по умолчанию преобразуется в widestrings, даже если это системная кодовая страница. Например, большинство Linux/Mac/BSD используют UTF-8 в качестве системной кодовой страницы. Передача -Fcutf8 в компилятор сохранит строковый литерал как widestrings.
398+FPC имеет очень ограниченную поддержку UTF-8. Фактически, FPC поддерживает хранение литералов только как 8-битные строки с кодировкой «по умолчанию» или как widestrings (широкие строки). Поэтому любая кодовая страница не по умолчанию преобразуется в widestrings, даже если это системная кодовая страница. Например, большинство Linux/Mac/BSD используют UTF-8 в качестве системной кодовой страницы. Передача -Fcutf8 в компилятор сохранит строковый литерал как widestrings.
412399
413-Во время выполнения widestring литерал конвертируется. Когда вы присваиваете литерал AnsiString, widestring литерал преобразуется с помощью widestringmanager в кодировку системы. По умолчанию widestringmanager под Unix просто конвертирует widechars в символы, уничтожая любые символы не ASCII. Вы должны использовать widestringmanager, например, cwstring, чтобы получить правильное преобразование. Модуль LazUTF8 делает это.
400+Во время выполнения widestring литерал конвертируется. Когда вы присваиваете литерал AnsiString, widestring литерал преобразуется с помощью widestringmanager в кодировку системы. По умолчанию widestringmanager под Unix просто конвертирует widechars в символы, уничтожая любые символы не ASCII. Вы должны использовать widestringmanager, например, cwstring, чтобы получить правильное преобразование. Модуль LazUTF8 делает это.
414401
415402# См. также
416403
417-Информация о Unicode, кодовых страницах, строковых типах и RTL в FPC.\
418-Обратите внимание, что эта информация не совсем полезна для системы Lazarus UTF-8, потому что с точки зрения FPC она является хаком и меняет кодовую страницу по умолчанию.\
404+Информация о Unicode, кодовых страницах, строковых типах и RTL в FPC.
405+Обратите внимание, что эта информация не совсем полезна для системы Lazarus UTF-8, потому что с точки зрения FPC она является хаком и меняет кодовую страницу по умолчанию.
419406[[FPC Unicode support/ru|Поддержка Юникода в FPC]]
420407
421408---
422-*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).*
409+*From the Free Pascal wiki, page "Unicode Support in Lazarus/ru", as the Internet Archive kept it on 2025-04-20, last changed on 2024-07-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