Error malformed utf 8 string

Модератор: Модераторы

Модератор: Модераторы

Error: Malformed UTF-8 string

Возникает на строке:

Код: Выделить всё
if MessageDlg('Удалить запись?',mtWarning,mbOKCancel,0)=mrOK then ...

Ниже в списке ошибок такая строка:

Код: Выделить всё
umain.pas(138,16) Error: UTF-8 code greater than 65535 found

Если пишу ‘Delete record’ — работает.
Почему это проявляется и как с ним бороться?
Win XP SP2/Lazarus 0.9.27 SVN Revision 17564M/FPC 2.2.3

ViruZ
постоялец
 
Сообщения: 175
Зарегистрирован: 30.05.2005 17:41:12
Откуда: Украина
  • Профиль
  • Сайт

Re: Error: Malformed UTF-8 string

Сообщение Mr.Smart » 25.11.2008 13:09:08

Lazarus 0.9.26 работает.

Mr.Smart
долгожитель
 
Сообщения: 1796
Зарегистрирован: 29.03.2008 01:01:11
Откуда: из леса!

Re: Error: Malformed UTF-8 string

Сообщение v-t-l » 26.11.2008 12:20:31

А кодировка исходника какая?

v-t-l
энтузиаст
 
Сообщения: 717
Зарегистрирован: 13.05.2007 16:27:22
Откуда: Belarus

Re: Error: Malformed UTF-8 string

Сообщение Vadim » 26.11.2008 14:13:57

ViruZ
Пользуйся системной функцией MessageBox():
if MessageBox(0, ‘Удалить запись?’,MB_ICONQUESTION+MB_OKCANCEL)=IDOK then …
Там надписи на кнопках автоматически подстраиваются под язык системы.

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: Yandex [Bot] и гости: 13

I’ve gotten a custom JSON api for my school, so I can look up homework. Let’s call the result string $result. Unfortunately it seems like the UTF-8 I’m getting back is malformed. Running mb_check_encoding confirms that the string is UTF-8 encoded, however I’m not able to decode the JSON string.

When I use json_decode on my string, it’s returning NULL and json_last_error() gives me the error «JSON_ERROR_UTF8» = ‘Malformed UTF-8 characters, possibly incorrectly encoded’.

using iconv("UTF-8", "UTF-8//TRANSLIT", $result)
is throwing a PHP notice:

iconv(): Detected an illegal character in input string in
C:wampwwwTietgenhomework.php

Using iconv("UTF-8", "UTF-8//IGNORE", $result) instead, is giving me a JSON result back, however it is being cut off in the middle (presumably at the same point where TRANSLIT is detecting the illegal character), giving me the JSON error

Unexpected control character found

Doing json_decode(utf8_encode($result)) is returning NULL aswell, and gives me this JSON error:

Syntax error, malformed JSON

echoing utf8_encode($result) is being cut off aswell.

I’ve got a program running on my comp called JSON Viewer. When I paste the JSON from the original source (the api, I’m accessing), it’s working perfectly in JSON Viewer.
enter image description here
This looks like the JSON is valid.

I’ve been stuck with this for a while, is there any way to fix an malformed UTF-8 string? if so, how? :)

Unfortunately I cannot share the API :/

Also, I can see that iconv is quite resource intensive, is there maybe a better method? :)

В этом разделе описаны сообщения, которые генерируются сканером. Сканер выполняет работу по проверке лексической структуры файлов Pascal, например, пытается найти зарезервированные слова, строки и т.п. Он также обрабатывает директивы и условия компиляции.

Сообщение

Описание

Fatal: Unexpected end of file

Это сообщение обычно появляется в одном из следующих случаев:

Исходный файл закончился, но оператор end. не был найден. Это происходит в основном, когда заявления begin и end не сбалансированы;

Подключаемый файл закончился на середине.

Комментарий не был закрыт

Fatal: String exceeds line

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

Fatal: illegal character «Сообщение1» (Сообщение2)

Неправильный символ был обнаружен во входном файле.

Fatal: Syntax error, «Символ1» expected but «Символ2» found

Это означает, что компилятор ожидал появления Символа1, но обнаружил Символ2. Это может случиться практически в любом месте (ошибка в языке Pascal).

Start reading includefile Сообщение1

Вы используете опцию –vt и компилятор говорит вам, что началось чтение подключаемого файла.

Warning: Comment level Сообщение found

Если используется опция –vw, то компилятор предупреждает вас, если находит вложенные комментарии. Вложенные комментарии не допускаются в Turbo Pascal и в Delphi, и могут вызвать ошибку в исходном коде.

Note: Ignored compiler switch «Сообщение1»

Если используется опция –vn, то компилятор предупреждает вас, если игнорирует этот переключатель.

Warning: Illegal compiler switch «Сообщение1»

Вы подключили директиву компилятора (например, {$…}), которую он не может распознать.

Warning: Misplaced global compiler switch

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

Error: Illegal char constant

Это сообщение появится, если вы укажете символ с помощью ASCII-кода, например, #96, но число будет слишком большое и лежать за пределами таблицы ASCII.

Fatal: Can’t open file «Сообщение1»

Free Pascal не может найти исходный файл программы или модуля, указанный вами в командной строке.

Fatal: Can’t open include file «Сообщение1»

Free Pascal не может найти исходный файл, указанный вами в скобках {$include..}.

Error: Illegal record alignment specifier «Сообщение1»

Вы указали {$PACKRECORDS n} или {$ALIGN n} с неправильным значением n. Для $PACKRECORDS правильные значения – это 1, 2, 4, 8, 16, 32, C, NORMAL, DEFAULT, а для $ALIGN – это значения 1, 2, 4, 8, 16, 32, ON, OFF. В режиме MacPas $ALIGN также поддерживает MAC68K, POWER и RESET.

Error: Illegal enum minimum-size specifier «arg1»

Вы указали {$PACKENUM n} с неправильным значением n. Только 1,2,4, NORMAL или DEFAULT являются правильными.

Error: $ENDIF expected for Сообщ1 Сообщ2 defined in Сообщ3 line Сообщ4

Ваши операторы условной компиляции не сбалансированы.

Error: Syntax error while parsing a conditional compiling expression

Это ошибка в выражении следующих директив компилятора: {$if ..}, {$ifc } или {$setc }.

Error: Evaluating a conditional compiling expression

Это ошибка в выражении следующих директив компилятора: {$if ..}, ifcorsetc.

Warning: Macro contents are limited to 255 characters in length

Содержимое макроса не может быть более 255 символов.

Error: ENDIF without IF(N)DEF

Ваши операторы {$IFDEF ..} and {$ENDIF} не сбалансированы.

Fatal: User defined: Сообщение1

Произошла ошибка, определённая пользователем. См. также «Руководство программиста».

Error: User defined: Сообщение1

Произошла ошибка, определённая пользователем. См. также «Руководство программиста».

Warning: User defined: Сообщение1

Предупреждение, определённое пользователем. См. также «Руководство программиста».

Note: User defined: Сообщение1

Замечание, определённое пользователем. См. также «Руководство программиста».

Hint: User defined: Сообщение1

Подсказка, определённая пользователем. См. также «Руководство программиста».

Info: User defined: Сообщение1

Информация, определённая пользователем. См. также «Руководство программиста».

Error: Keyword redefined as macro has no effect

Вы не можете переопределить ключевые слова в макросе.

Fatal: Macro buffer overflow while reading or expanding a macro

Ваш макрос или результат его работы слишком большие для компилятора.

Warning: Expanding of macros exceeds a depth of 16.

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

Warning: compiler switches aren’t supported in // styled comments

Переключатели компилятора установлены нормальном стиле комментариев Pascal, и не поддерживают стиль С++ (//).

Handling switch «Сообщение1»

Если вы включили отладочную информацию (-vd), то компилятор сообщает вам, если он оценивает условия операторов компиляции.

ENDIF Сообщение1 found

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

IFDEF Сообщение1 found, arg2

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

IFOPT Сообщение1 found, Сообщение2

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

IF Сообщение1 found, Сообщение2

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

IFNDEF Сообщение1 found, Сообщение2

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

ELSE Сообщение1 found, Сообщение2

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы.

Skipping until…

Если вы включили условные сообщения (-vc), то компилятор выдаёт сообщение, если находит условные операторы, и какие части он пропускает или компилирует.

Info: Press <return> to continue

Если используется опция –vi, то компилятор останавливает компиляцию, если обнаруживает директиву {$STOP} и ожидает, пока не будет нажата клавиша ENTER.

Warning: Unsupported switch «Сообщение»

Если включены предупреждения (-vw), то компилятор предупреждает вас о неподдерживаемых переключателях. Это означает, что данные переключатели используются в Delphi или Turbo Pascal, но не используются в Free Pascal.

Warning: Illegal compiler directive «Сообщение»

Если включены предупреждения (-vw), то компилятор предупреждает вас о нераспознанных переключателях. Список распознаваемых переключателей см. в руководстве программиста.

Back in Сообщение

Если вы используете переключатель –vt, то компилятор говорит вам, когда он заканчивает читать подключаемый файл.

Warning: Unsupported application type: «Сообщение»

Вы получите это предупреждение, если вы указали неизвестный тип приложения с директивой {$APPTYPE}.

Warning: APPTYPE is not supported by the target OS

Директива {$APPTYPE} поддерживается только определёнными операционными системами.

Warning: DESCRIPTION is not supported by the target OS

Директива {$DESCRIPTION} не поддерживается целевой операционной системой.

Note: VERSION is not supported by target OS

Директива {$VERSION} не поддерживается целевой операционной системой.

Note: VERSION only for exes or DLLs

Директива {$VERSION} используется только для исполняемых файлов или исходных кодов DLL.

Warning: Wrong format for VERSION directive «Сообщение»

Неправильный формат директивы {$VERSION}. Директива должна иметь формат ПолнаяВерсия.СокращённаяВерсия, где ПолнаяВерсия и СокращённаяВерсия – это слова.

Error: Illegal assembler style specified «Сообщение»

Если вы указали режим ассемблера директивой {$ASMMODE xxx}, а компилятор не распознал указанный вами режим.

Warning: ASM reader switch is not possible inside asm statement, «Сообщение» will be effective only for next

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

Error: Wrong switch toggle, use ON/OFF or +/­

Вы должны использовать ON или OFF или + или для переключателя.

Error: Resource files are not supported for this target

Целевая платформа, для которой выполняется компиляция, не поддерживает файлы ресурсов.

Warning: Include environment «Сообщение» not found in environment

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

Error: Illegal value for FPU register limit

Правильные значения для этой директивы 0…8 и NORMAL/DEFAULT.

Warning: Only one resource file is supported for this target

Целевая платформа, для которой выполняется компиляция, поддерживает только один файл ресурсов. Первый найденный файл ресурсов будет использоваться, остальные игнорируются.

Warning: Macro support has been turned off

Объявление макроса было найдено, но поддержка макросов отключена, поэтому макрос будет игнорироваться. Включение поддержки макросов при компиляции выполняется опцией –Sm в командной строке или директивой {$MACRO ON} в исходном коде.

Error: Illegal interface type specified. Valids are COM, CORBA or DEFAULT.

Указанный тип интерфейса не поддерживается.

Warning: APPID is only supported for PalmOS

Директива {$APPID} поддерживается только для целевой платформы PalmOS.

Warning: APPNAME is only supported for PalmOS

Директива {$APPNAME} поддерживается только для целевой платформы PalmOS.

Error: Constant strings can’t be longer than 255 chars

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

Fatal: Including include files exceeds a depth of 16.

Если используются подключаемые файлы, то уровень вложенности должен быть не более 16. Компилятор не будет дальше разворачивать файлы, потому что это может означать, что используется рекурсия.

Fatal: Too many levels of PUSH

Максимально допускается 20 уровней при использовании инструкции PUSH. Эта ошибка возможна только в режиме MacPas.

Error: A POP without a preceding PUSH

Эта ошибка возможна только в режиме MacPas.

Error: Macro or compile time variable «Сообщение» does not have any value

Выражение условной компиляции нельзя вычислить.

Error: Wrong switch toggle, use ON/OFF/DEFAULT or +/-/*

Вы должны использовать ON или OFF или DEFAULT или + или или * для данного переключателя.

Error: Mode switch «Сообщение» not allowed here

Переключатель режима уже был использован, или, в случае с опцией –Mmacpas, переключатель режима обнаружен после UNIT.

Error: Compile time variable or macro «Сообщение» is not defined.

Выражение условной компиляции нельзя вычислить. Только в режиме MacPas.

Error: UTF-8 code greater than 65535 found

Free Pascal обрабатывает строки UTF-8 как widestrings, то есть код символа ограничен 65535.

Error: Malformed UTF-8 string

Данная строка не является правильной строкой UTF-8.

UTF-8 signature found, using UTF-8 encoding

Компилятор нашёл закодированную сигнатуру UTF-8 ($ef, $bb, $bf) в начале файла, поэтому будет интерпретировать его как файл UTF-8.

Error: Compile time expression: Wanted Сообщ1 but got Сообщ2 at Сообщ3

Во время компиляции обнаружено неправильное выражение при проверке типов.

Note: APPTYPE is not supported by the target OS

Директива {$APPTYPE} поддерживается только определённой операционной системой.

Error: Illegal optimization specified «Сообщение»

Вы указали оптимизацию директивой {$OPTIMIZATION xxx}, но компилятор не распознаёт указанную вами оптимизацию.

Warning: SETPEFLAGS is not supported by the target OS

Директива {$SETPEFLAGS} не поддерживается целевой операционной системой.

Warning: IMAGEBASE is not supported by the target OS

Директива {$IMAGEBASE} не поддерживается целевой операционной системой.

Warning: MINSTACKSIZE is not supported by the target OS

Директива {$MINSTACKSIZE} не поддерживается целевой операционной системой.

Warning: MAXSTACKSIZE is not supported by the target OS

Директива {$MAXSTACKSIZE} не поддерживается целевой операционной системой.

Error: Illegal state for $WARN directive

Только ON и OFF можно использовать как значения с директивой компилятора {$WARN}.

Error: Illegal set packing value

Только 0, 1, 2, 4, 8, DEFAULT и NORMAL распознаются как параметры упаковки.

Warning: PIC directive or switch ignored

Некоторые целевые ОС, такие как WINDOWS, не поддерживают и не нуждаются в PIC, поэтому директива PIC игнорируется.

Warning: The switch «Сообщение» is not supported by the currently selected target

Некоторые переключатели компилятора, такие как $E, не поддерживаются на всех целевых ОС.

Warning: Framework-related options are only supported for Darwin/Mac OS X

Фреймворк является неизвестной концепцией, или не поддерживается FPC, или операционная система отличается от Darwin/Mac OS X.

Error: Illegal minimal floating point constant precision «Сообщение»

Правильная минимальная точность для плавающей точки может быть константой по умолчанию, 32 и 64, что означает соответственно минимум (обычно 32 бит), 32 бита и 64 бита точности.

Warning: Overriding name of «main» procedure multiple times, was previously set to «Сообщение»

Для главной процедуры указано более одного имени. Только последнее имя будет использоваться.

Warning: Illegal identifier ”arg1” for $WARN directive

Identifier is not known by a {$WARN} compiler directive.

Идентификатор не известна {$WARN} директивы компилятора.

Error: Illegal alignment directive

The alignment directive is not valid. Either the alignment type is not known or the alignment value is not a power of two.

Директива выравнивание не является действительным. Или тип выравнивания не известно или значение выравнивание не является степенью двойки.

Fatal: It is not possible to include a file that starts with an UTF-8 BOM in a module that uses a different code page

All source code that is part of a single compilation entity (program, library, unit) must be encoded in the same code page

Весь исходный код, что является частью единого целого компиляции (программа, библиотека, единицу) должны быть закодированы в той же кодовой страницы

Добрый вечер.

У меня программа на Delphi 2005 (по некоторым причинам прямо сейчас немогу поменять на что то другое)
программа хорошо работает с MS Access + ADO
Сама программа используется в нескольких странах.

Сейчас я портирую ее на Zeos (я читал уже как это называют здесь) + Firebird 2.5
Кстати, Zeos 6.6 официально работает только с Firebird 2.1
Но я сделал следующее:
1. создал базу в Firebird 2.1
2. использую fbclient отFirebird 2.1.
3. реальный же сервер это Firebird 2.5
Все работает отлично.
Я использу 2.5 и не 2.1 т.к. мне нужны некоторые функции 2.5

Но проблема в другом
например, для старой программы Delphi 2005 + ADO + Access
все работает нормально. я могу по русски написать текст в программе и он коректно запишется в базу.
например таблица
COMPANY
companyid: integer
name: varchar(50)

например я вставляю 2 строки
Тестовая компания 1
тестовая компания 2

и затем делаю запрос (из Access)
select * from company where name like ‘%те%’

он возвращает 2 строки (как и должно быть).

но когда я использую Delphi 2005 + Zeos 6.6+ Firebird 2.5 + database UTF8
после попытки запостить русский текст получаю ошибку «malformed string»
т.к. Firebird server не знает как сконвертировать из русского текста (не юникодного) в UTF8

если я создаю базу с Charset NONE тогда я могу записывать в базу русский текст
но приведенный выше пример не работает. он возращает только одну строку.

при этом Firebird-кие функции Lower и Upper не работают, когда Charset NONE для русского текста.

Вопрос как достичь поведения как в Delphi 2005 + ADO + Access
т.е. чтобы collation работал и чтобы я мог вставлять русский текст (ну и тексты на других языках, для представления которы не нужет unicode). т.е. чтобы, например, в Греции человек мог нормально вести базу на греческом, а араб — на арабском

сейчас я использую ZConnection без параметров соединения
я нашел опции типа

character_set_client=utf8
character_set_connection=utf8
character_set_database=utf8
character_set_results=utf8
character_set_server=utf8
character_set_system=utf8
collation_connection=utf8_general_ci
collation_database=utf8_general_ci
collation_server=utf8_general_ci

Codepage=utf8

Charset=UTF8

подставлял разные значения кодировок, то ничего не работает.

хорошая статья про кодировки
http://www.destructor.de/firebird/charsets.htm
идеально мне нужна база данных в utf8 но чтобы приложение было не юникодным (написанным на D2005)

понятно, что в этой цепочке много несуразного: Zeos сам по себе, не юникодный D2005
от всего этого буду уходить, просто хотелось бы более быстрое решение.

буду глубоко признателем любым советам.

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь

Составитель: KDV, 12.12.2008, последнее обновление – 15.03.2015.
Информация предоставлена: dimitr, hvlad
Благодарности: shmel, Janex, Таблоид, shaposh

 

Unicode. Что это и зачем это нужно?

Unicode – это способ представления разных национальных символов в универсальном формате. На вопрос «зачем это нужно» можно ответить следующим образом – если вы не планируете переводить ваши приложения на другие национальные языки (немецкий, французский и т. п.), то Unicode вам не нужен. В этом случае вы спокойно используете базу данных в WIN1251, с аналогичным параметром коннекта, и работаете как и раньше.

Если же планируется (даже только в перспективе) создавать приложения, работающие одновременно с несколькими кодовыми страницами, или с многобайтовыми кодировками, то тогда Unicode – ваш вариант.

  • О Unicode вообще
  • Поддержка Unicode в Delphi 2009 и C++Bulder 2009 – часть 1, часть 2, часть 3
  • Юникод в Delphi 2009
  • Перечень проблем, которые могут возникнуть при переносе кода на Delphi 2009, 2010 или XE с предыдущих версий

Также крайне рекомендуется для чтения электронные книги, доступные бесплатно покупателям Delphi 2009, 2010 и XE:

  • Marco Cantu, Delphi 2009 Handbook PDF eBook
  • Marco Cantu, Delphi 2010 Handbook

Какие операционные системы поддерживают Unicode?

Windows – все последние, кроме 95, 98, ME. В этих ОС поддержка unicode неполная, поэтому, в частности, утверждается, что Delphi 2009, 2010, XE и XE2 и приложения, на них написанные, не будут работать в Windows 95, 98 и ME.

Linux – см. Unicode FAQ.

Что нужно для работы с Unicode?

Для начала нужна СУБД с поддержкой Unicode. Firebird с версии 2.0 и InterBase с версии 2007 поддерживают кодировку UTF-8, которая является полноценной реализацией формата для хранения Unicode-символов.

Далее, потребуется среда разработки и компоненты или драйверы, поддерживающие Unicode.

Первые среды разработки с полной поддержкой Unicode – Delphi и C++Builder начиная с версии 2009 (и далее 2010, XE-XE7). Если вы пока еще используете предыдущие версии Delphi и BCB (2007 и ниже), то использование unicode будет проблематичным – вам придется не только использовать специальные компоненты (вроде TMS), но и компоненты доступа к БД или драйверы, которые также поддерживают Unicode.

С каких версий InterBase и Firebird поддерживают UTF8?

InterBase – с 2007. Firebird – с 2.0.

При этом, разумеется, база данных с поддержкой UTF8 может быть создана только в этих версиях, поскольку UTF8 поддерживается не только кодом сервера, но и таблицей rdb$character_sets в базе данных.

Например, если взять базу данных от Firebird 1.5, и открыть в Firebird 2.x, то поддержки UTF8 в этой базе данных не будет. Чтобы возможность использовать в БД UTF8 появилась, нужно ей сделать backup и restore (при restore будет создана новая БД в новом формате со старыми данными).

Поддержка UTF8 в InterBase и Firebird одинаковая?

К сожалению, нет. Причем, неодинаковых аспектов много.

В rdb$character_sets кодировка UTF8 имеет в Firebird идентификатор 4 (это был свободный номер рядом с давно имеющейся в IB и FB кодировкой UNICODE_FSS с ID = 3), а в InterBase – 59. Код 59 в Firebird имеет кодировка WIN1256. То есть, разработчики InterBase не ставили перед собой вопрос обеспечения совместимости с Firebird в этом плане.

Начиная с Firebird 1.5 при получении строковых данных CHAR и VARCHAR с сервера, если это не кодировки NONE или OCTETS, и если кодировка коннекта не NONE, то в sqlsubtype передается: в старшем байте код collate, а в младшем – код character set.

В InterBase также передается код collate в sqlsubtype, однако, с какой именно версии, неизвестно. Возможно, с 2009, т. к. IBX в Delphi 2010 содержит изменения на эту тему (см. в конце FAQ).

Чем отличаются кодировки UNICODE_FSS и UTF8?

Это несколько разные реализации поддержки Unicode.

UNICODE_FSS реализует более старую версию стандарта Unicode, которая ограничена 3-мя байтами на символ (поэтому, теоретически, поддерживает не все языки), для нее нет алфавитных наборов сортировок, в версиях ниже Firebird 2.5 для нее не проверяется корректность введенного текста с точки зрения unicode.

UTF8 поддерживает последнюю версию стандарта Unicode, до 4 байт на символ, все вышеперечисленные недостатки отсутствуют.

А можно использовать UNICODE_FSS вместо или если нет UTF8?

Только если вы не предполагаете переходить на современные версии InterBase и Firebird, которые поддерживают UTF8. Недостатки UNICODE_FSS перечислены в предыдущих пунктах.

Как создать базу в юникоде?

Для этого достаточно при создании БД указать character set UTF8. После этого по умолчанию все создаваемые строковые поля и переменные (varchar, char) таблиц, процедур, триггеров и т.д. будут иметь кодировку UTF8, если вы не укажете другую специально.

Как сохранять данные в разных кодировках?

Есть несколько вариантов:

  • Старый и неудобный: можно делать с любыми версиями InterBase и Firebird: создаете столбец в таблице, указывая нужный character set, затем при подсоединении к БД указываете этот же чарсет. Не очень удобно, потому что для каждого языка (чарсета) приложение должно показывать пользователю свой «набор» строковых столбцов.
  • Современный и простой: создать базу в UTF8 и сохранять данные либо в конкретном character set, либо сразу передавать данные в UTF8. Для передачи данных на сервер в UTF8 нужно, чтобы драйверы или компоненты доступа делали это «прозрачно» для приложения. См. пункт.

 

Я могу работать с базой в UTF8 через WIN1251?

Разумеется, для этого достаточно указать чарсет соединения WIN1251. Данные будут идти на сервер в 1251, и автоматически перекодироваться в UTF8 при сохранении (при чтении – перекодироваться обратно в win1251). Это самый легкий вариант начала работы с юникодом. Также это подходящий вариант, если используете Delphi версии 2007 и ниже, и вы не хотите использовать никакие компоненты Unicode (например, tms), но планы перехода на Unicode есть.

Кстати, это не специальная особенность WIN1251 и UTF8. Вы можете использовать любую национальную кодировку точно таким же образом.

А говорят, что в Unicode строки занимают больше места?

Да, причем зависит от самих символов, которые находятся в строках.

UTF8 это формат с плавающим размером символа, от 1 до 4 байт. В частности, символы русских букв в 1251 занимают по 2 байта, а «стандартные» английские буквы с кодом от 32 до 127 занимают 1 байт.

Пример. Создадим таблицу со столбцом varchar(30) в win1251, и еще таблицу со столбцом varchar(30) в utf8. Зальем в первую таблицу 100 тысяч записей, со случайными символами в кодировке win1251 от А до я, длиной от 10 до 30 символов. Затем перельем (insert into … select from) эти данные во вторую таблицу. При этом данные корректно отконвертируются из 1251 в utf8. В итоге, обе таблицы в IBAnalyst будут показаны как:

Table Records RecLength VerLen Versions Max Vers Data Pages Size, mb Slots Avg fill% RealFill
X1251 100000 28.86 0.00 0 0 852 6.66 852 66 66
XUTF8 100000 49.01 0.00 0 0 1094 8.55 1094 74 74

Интерпретировать результат можно следующим образом:

  • вторая половина кодовой таблицы символов win1251 сохраняется в utf8 в двух байтах. Первая – в одном. В тесте были использованы символы из второй половины кодовой таблицы.
  • размер таблицы (столбца) в utf8 примерно 2 раза больше, чем размер таблицы (столбца) в win1251. Чуть меньше чем в 2 раза потому, что используется сжатие заголовков записей, и т. п. То есть, будучи взятыми «чистыми», данные в utf8 для русских букв win1251 будут занимать в 2 раза больше места, чем в win1251.

Так что, в зависимости от кодировки, данные могут занимать в БД разный объем.

Максимальный размер ключа индекса

Ранее в InterBase и в Firebird размер строкового столбца, по которому можно построить индекс, например для кодировки WIN1251 был ограничен 252 символа без collate, и 84 символа с collate (об этом написано в «О работе с русскими буквами в InterBase/Firebird»).

В Firebird 2.0 и InterBase XE допустимый размер такого столбца был увеличен, и сейчас представляет собой 1/4 размера страницы. Например, при странице 4096 байт максимальный размер индексируемого строкового столбца равен 1024.

Однако, с кодировками, где количество байт на символ выше 1, это не так. Для столбца в кодировке UTF8 и collate unicode_ci при странице 4096 байт максимальный размер составляет 169 символов, поскольку при сортировках ci один символ кодируется 6 байтами. Для страницы 8192 и 16384 байт максимальный размер индексируемого с таким collate столбца будет, соответственно, в 2 и 4 раза больше.

Длина строковых столбцов и изменение ее размера

В зависимости от кодировки символы могут занимать в строке 1 и более байт. Для кодировок NONE, WIN1251 и т.п. это действительно 1 байт. Поэтому, если в char(15) или varchar(15) находится слово из 10 букв, то это слово будет занимать 10 байт. Но для некоторых кодировок это может быть и 2, 3, 4 байта на символ. Количество байт на символ в конкретной кодировке записано в системной таблице RDB$CHARACTER_SETS, проще всего посмотреть запросом

select rdb$character_set_id, rdb$character_set_name, rdb$bytes_per_character
from rdb$character_sets

Результат будет примерно таким (примерно потому, что идентификатор кодировки UTF8 и некоторых других, как и набор кодировок, у Firebird и InterBase отличаются, см. этот FAQ далее)

RDB$CHARACTER_SET_ID RDB$CHARACTER_SET_NAME RDB$BYTES_PER_CHARACTER
0 NONE 1
1 OCTETS 1
2 ASCII 1
3 UNICODE_FSS 3
4 UTF8 4
48 DOS866 1
51 WIN1250 1
52 WIN1251 1
53 WIN1252 1
54 WIN1253 1
55 WIN1254 1
63 KOI8R 1
64 KOI8U 1
   

То есть, когда вы создаете в таблице столбец

NAME VARCHAR(10) CHARACTER SET UTF8

Firebird (и InterBase) умножают размер столбца 10 символов на 4 байта на символ, и сохраняют информацию о максимальной длине строки в символах и в байтах в системных таблицах.

Информация о размере столбца хранится в системной таблице RDB$FIELDS (см. полезные запросы к системным таблицам), как для доменов, так и для обычных столбцов:

  • RDB$FIELD_LENGTH – размер столбца в байтах. Т.е. для примера выше в этом столбце будет записано 40, а не 10.
  • RDB$CHARACTER_LENGTH – размер столбца в символах. Для примера выше там будет 10, а не 40.
  • RDB$CHARACTER_SET_ID – идентификатор чарсета, по которому можно выяснить, почему в field_length значение отличается от character_length.

Если вы попытаетесь уменьшить размер столбца командой

alter table … alter column name type varchar(9),

то будет выдано сообщение об ошибке New size specified for column FIRST_NAME must be at least 40 characters.

Это сообщение на самом деле ошибочно (CORE-8394), потому что в нем говорится о characters, а выводится длина в байтах на символ. Должно быть, разумеется, «at least 10 characters».

А как сортируются данные в юникоде?

У InterBase для UTF8 есть только одна сортировка – бинарная UTF8.

В Firebird у кодировки UTF8 есть несколько наборов сортировок (collate) (см. далее). Пользоваться ими можно точно так же, как и с любыми другими наборами сортировок – или указывать collate в объявлении столбца таблицы, или указывать collate в order by, where, и т.д. Примеры есть в FAQ по работе с win1251.

Пример порядка сортировки русских букв столбца UTF8 с умолчательным collate (точно так же как win1251): А, Б, В, …а, б, в…

Пример порядка сортировки русских букв столбца UTF8 с collate UNICODE (order by … collate UNICODE – эквивалент order by … collate pxw_cyrl): а, А, б, Б, в, В, …

Помните, что при сортировке строк значение также имеет длина строки. Поэтому в последнем случае, не смотря на то что по одной букве б идет перед Б, строка «бб» будет идти после строки «Б».

Какие есть сортировки (collate) для UTF8?

В InterBase нет никаких сортировок для UTF8, кроме бинарной UTF8.

В Firebird есть 5 сортировок:

  • UTF8 – по умолчанию, бинарная сортировка
  • UCS_BASIC – то же самое (для соответствия стандарту SQL)
  • UNICODE – алфавитная сортировка
  • UNICODE_CI – алфавитная сортировка для регистронечувствительного поиска (появилась в Firebird 2.1).
  • UNICODE_CI_AI – алфавитная сортировка для регистронечувствительного поиска с игнорированием акцентированных символов (появилась в Firebird 2.5). Для русского языка действует только в отношении букв е и ё, но на их порядок сортировки не влияет.

Пример сортировки смотрите выше.

Пример для unicode_CI_AI. Допустим, есть таблица TEST, в которой в столбце NAME записаны поодиночке все буквы русского алфавита, строчные и прописные.

select * from test where name = ‘ё’

будет выдана только строка с ё

select * from test where name collate unicode_ci = ‘ё’

будут выданы строки с ё и Ё

select * from test where name collate unicode_ci_ai = ‘ё’

будут выданы строки с ё, Ё, е и Е. Тот же результат будет и при

where name collate unicode_ci_ai = ‘е’
 

Внимание! В версиях 2.1 и 2.5 есть ряд багов, связанных с unicode_ci, как исправленных, так и нет. Например, 2457, 3239, 1989. Поэтому использовать unicode_ci в версиях ниже 2.5.1 не рекомендуется.

 

Как работает upper в юникоде?

Нормально. Для русских букв таблица перевода прописных в строчные (upper) и обратно (lower) работает как для бинарной сортировки UTF8 так и для любых других имеющихся сортировок. То есть, указывать collate для столбцов при вызове upper/lower не нужно.

Регистронезависимый поиск

Можно организовать двумя способами.

По старинке – при помощи upper, даже если столбцы не имеют collate unicode/unicode_ci. Например,

select * from table
where upper(name) = ‘СТРОКА’

Естественным для Unicode образом – с collate unicode_ci и unicode_ci_ai (см. пункт выше!) можно проще:

  1. объявляем столбец как

name varchar(30) collate unicode_ci

  1. делаем поиск как

select * from table
where name = ‘строка’

При этом, например, если есть записи с ‘а’ и ‘А’, то они будут выданы все независимо от того, в строке поиска указано ‘а’ или ‘А’. То есть, поиск получается регистронезависимым. Более того, если есть индекс по столбцу name collate unicode_ci, то он будет использован оптимизатором для поиска.

Если же столбец уже создан и не имеет collate (или имеет collate unicode), то «превратить» его в регистронечувствительный можно указанием collate unicode_ci:

select * from table
where name collate unicode_ci = ‘строка’

Однако, если уже есть индекс по name, то он использоваться не будет.

Дополнительно, для сортировки unicode_ci_ai символы е, Е, ё, Ё считаются одинаковыми. Например, запрос

select * from table
where name collate unicode_ci_ai containing ‘е’

выдаст все строки, содержащие в столбце name буквы е, Е, ё, Ё. Тот же результат будет и при

where name collate unicode_ci_ai containing ‘ё’
 

Внимание! В версиях 2.1 и 2.5 есть ряд багов, связанных с unicode_ci, как исправленных, так и нет. Например, 2457, 3239, 1989. Поэтому использовать unicode_ci в версиях ниже 2.5.1 не рекомендуется.

 

Я создал базу и таблицы в UTF8, подсоединился в UTF8, и получаю ошибку Malformed string

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

Можно ли использовать при коннекте чарсет NONE?

Да, можно, но при этом нужно учитывать, что кодировка должна быть указана для любых строковых данных, как констант, так и параметров, передаваемых в запросе. Например, если сделать попытку выполнить запрос

select * from table
where name collate unicode_ci = ‘строка’

с чарсетом коннекта NONE, то будет выдана ошибка Malformed string. Обратите внимание, что речь идет именно о символах win1251, т.е. тех, которые в однобайтовой кодировке имеют код больше 127, и в unicode кодируются двумя байтами на символ. Если заменить ‘строка’ на ‘string’ (т. е. русские на латинские символы), то ошибки не будет.

Поэтому, если константа действительно содержит символы кодировки 1251, то это нужно явно указать в запросе

select * from table
where name collate unicode_ci = _win1251 ‘строка’

 

А почему IBExpert тоже дает ошибку Malformed sting?

Такое может быть в старых версиях IBExpert, или в новых версиях, где при выборе чарсета коннекта UTF8 рядом не снята галочка с пункта «Do NOT perform conversion from/to UTF8.

Поэтому в новой версии IBExpert перед коннектом к БД в utf8 в Database Registration Info сначала выключите указанную галку, затем делайте соединение. Для старых версий IBExpert есть варианты:

Написать запрос в SQL Editor, затем в меню по правой кнопке мыши вызвать Convert to UNICODE, и только затем выполнить запрос.

Или – перед строковыми константами в запросе нужно указывать явно их кодировку. Например,

select * from table
where name collate unicode_ci = _win1251 ‘строка’

Также это будет работать, если чарсет коннекта указан как none. При чарсете коннекта win1251 запрос уйдет на сервер в корректной кодировке, и данные будут корректно преобразованы сервером из win1251 в utf8 для сравнения name и строковой константы.

Как мне сконвертировать базу WIN1251 в UTF8?

Это можно сделать только копированием данных из одной базы в другую:

  1. создать новую базу в UTF8
  2. извлечь скрипт метаданных из БД win1251, убрать оттуда все упоминания WIN1251 (помните – у вас могут быть проблемы с несоответствием сортировок в WIN1251 и UTF8), и применить этот скрипт на базе в UTF8
  3. перенести все данные каким-нибудь инструментом, вроде IBPump. Такому инструменту не надо уметь поддерживать юникод, т. к. при записи данных в чарсете коннекта win1251 сервер сам преобразует их в UTF8.

 

Какую кодировку использовать в скриптах?

К скриптам есть несколько требований.

В начале скрипта до коннекта к БД должно быть указание SET NAMES …;, совпадающее с кодировкой символов, которые используются в скрипте.

Например, если текстовый файл содержит символы 1251, то тогда в начале скрипта должно быть

SET NAMES WIN1251;

Если текстовый файл содержит символы в Unicode, то тогда в начале скрипта должно быть написано

SET NAMES UTF8;

При этом, сам файл должен содержать символы именно в юникоде. Например, для «Блокнота» (Notepad) это делается следующим образом – открываете текстовый файл, «Сохранить как» – выбираете кодировку UTF-8 (по умолчанию текстовые файлы Блокнот создает в ansi, если там явно нет символов Unicode).

После этого национальные символы, вводимые при редактировании этого файла в Блокноте, будут в UTF8.

Ошибка Division by zero в IBX Delphi 2009 и выше

Баг зарегистрирован в QualityCentral под номером 68103.

Причина – в кривом методе

function TIBXSQLVAR.GetCharsetSize: Integer;

модуля IBSQL.

В IBX, поставляемом с Delphi до версии 2009, до сих пор не было вообще никакой поддержки Unicode. В Delphi 2009 появилась поддержка unicode на уровне рантайма, и также был изменен IBX.

Метод GetCharsetSize появился с целью поддержки unicode, но идентификаторы кодировок InterBase в нем зашиты жестко, что приводит к несовместимости с идентификаторами кодировок в Firebird (см. отличия в поддержке UTF8), а также к некорректной обработке SQLSubtype, где и у InterBase (!) и у Firebird в старшем байте может содержаться код collate столбца. Более правильно было бы определять количество байт на символ обращаясь к столбцу RDB$BYTES_PER_CHARACTER таблицы RDB$CHARACTER_SETS. Это не только унифицировало бы поддержку InterBase и Firebird, но и обеспечило бы совместимость IBX со всеми будущими версиями InterBase, если бы в них появлялись новые кодировки. Однако такая реализация потребовала бы «кэширования» данной информации, чтобы не происходило обращение к этим таблицам каждый раз при вызове GetCharsetSize.

Пример скорректированного для Firebird кода GetCharsetSize (модуль IBSQL.pas):

function TIBXSQLVAR.GetCharsetSize: Integer;
begin

case SQLVar.SQLSubtype and $FF of // здесь and $FF убирает id collate, возвращаемый Firebird

0, 1, 2, 10, 11, 12, 13, 14, 19, 21, 22, 39,
45, 46, 47, 50, 51, 52, 53, 54, 55, 58 : Result := 1;
5, 6, 8, 44, 56, 57, 64 : Result := 2;
3 : Result := 3;
4, 59 : Result := 4; // здесь правильно обрабатывается id UTF8 в Firebird и InterBase

else

Result := 0;

end;

end;
 

Замечание от 28.01.2010. В Delphi 2010 в этой функции есть небольшие исправления, в частности, SQLSubtype and $FF, но идентификатор utf-8 Firebird (код 4) так и не поддерживается. Поэтому Вам все равно придется исправлять строку
59 : Result := 4;
на
4, 59 : Result := 4;

 

Как использовать Unicode (UTF8) в UDF?

При написании UDF на Delphi для работы с UTF8 есть несколько условий:

  • компилировать UDF нужно в Delphi 2009 и выше (2010, XE-XE7). Можно написать такую UDF и в версиях 2007 и ниже, которые не поддерживают Unicode на уровне RTL и VCL, но код будет выглядеть достаточно сложным;
  • во входных и выходных параметрах передаются строки в UTF8, но это строки InterBase или Firebird, поэтому их нужно «приводить» к строкам Delphi;
  • внутри udf в Delphi 2009-XE7 строки можно обрабатывать как UTF8String, или любой другой кодировке, не забывая предыдущий пункт;
  • при аллокировании памяти для результата в функциях с FREE_IT необходимо учитывать, что Unicode-символы в формате UTF-8 могут иметь длину от 1 до 4 байт, поэтому нужно использовать правильные функции для определения длины Unicode-строки.

Вот пример функции, которая принимает UTF8 строку, добавляет к ней текст в unicode, и возвращает UTF8 строку:

DECLARE EXTERNAL FUNCTION utf8ex
CSTRING(80)
RETURNS CSTRING(80) FREE_IT
ENTRY_POINT ‘utf8example’ MODULE_NAME ‘utf8func’
uses ib_util;

function utf8example(P1: PAnsiChar): PAnsiChar; cdecl; export;
// параметры объявлены как PAnsiChar, потому что теперь в D2009/2010 обычный
// PChar это PWideChar, что потребовало бы дополнительного приведения типов
var u: UTF8String; // переменная для работы с unicode в формате utf-8 в Delphi
begin

u:=UTF8String(p1) + ‘привет’; // в u получаем склеенную строку в UTF8
Result:=ib_util_malloc(Length(u)+1); // Length(u) вычислит правильный размер строки, плюс байт для #0
StrCopy(Result, PAnsiChar(u)); // копируем utf8 строку в переменную результата

end;

Можно было бы использовать и другой тип строки для переменной u, но это потребует дополнительного приведения результата к UTF8String. Этот пример дан как наиболее простой для Delphi 2009-XE7.

Примечание. Если мы создали эту функцию в базе данных, у которой чарсет по умолчанию задан UTF8, то она (udf) будет работать корректно даже если ей на вход передать данные в кодировке 1251 или любой другой. Поскольку мы не задавали чарсет параметров функции в «declare external…», он будет идентичен умолчательному, то есть UTF8, и данные в любой другой кодировке будут автоматически перекодированы сервером как перед передачей в виде параметра, так и после получения результата от функции.

Если же создать эту функцию в базе данных с любым другим чарсетом по умолчанию, то эта функция будет работать некорректно, если только при ее объявлении не указать у параметров CHARACTER SET UTF8.

Интересующимся работой с unicode в Delphi рекомендуем книгу Марко Канту Delphi 2009 Handbook, которая доступна в виде pdf бесплатно для зарегистрированных пользователей Delphi 2009 и 2010.

Есть проблемы со старыми UDF в базе UTF8

Да, поскольку практически все известные старые библиотеки функций (rfunc, freeudflib и т.п.) обрабатывали строки как однобайтные наборы символов, без учета кодировок вообще. А российские разработчики писали свои udf, предполагая что кодировка базы будет всегда или none или win1251. Поэтому, разумеется, все эти функции не умеют работать с unicode (многобайтными кодировками).

Так что, нужные функции придется переписывать, как в примере выше.

Временно решить проблему, если первое время в базе в utf8-строках будет хранится только информация, полученная из 1251, можно объявив такие udf с указанием для их строковых параметров character set win1251.

В этом случае сервер автоматически перекодирует utf8 в 1251 и обратно при вызове udf. Однако, как только в такую udf будут переданы символы, несовместимые с 1251, сервер сообщит об ошибке.

(c) iBase.ru, 2002-2015.
Запрещается перепечатка, перевод и копирование. Разрешается частичное цитирование с обязательной ссылкой на источник – www.ibase.ru/unicode_faq/

Понравилась статья? Поделить с друзьями:
  • Error malformed statement
  • Error malformed list on input
  • Error malformed database schema
  • Error making static c
  • Error making sdcard fox directory required key not available