SASGIS - SAS.Планета
View Issue Details
0002690SAS.ПланетаРефакторингpublic21-04-2015 09:2807-11-2015 19:17
vdemidov 
vdemidov 
normalminorhave not tried
resolvedfixed 
141212 
151111151111 
0002690: Добавить определение кодировки ini-файлов при загрузке в ConfigDataProvider
Сейчас программа при попытке загрузить ini-файл в юникодной кодировке, не загружает ничего. Нужно при загрузке использовать DetectUTFEncoding и конвертировать полученное в текущий тип String.
No tags attached.
related to 0002877resolved vdemidov Поддержка utf-8 и utf-16 при загрузке параметров карты в неюникодной версии программы 
child of 0002166resolved zed Переход на версию Delphi с полной поддержкой юникода 
child of 0002875resolved vdemidov Символы не из основной локали заменяются на знаки вопроса в истории поиска 
child of 0002878resolved vdemidov В юникодной версии невозможно сохранить путь к кэшу в юникоде 
Issue History
21-04-2015 09:28vdemidovNew Issue
21-04-2015 09:29vdemidovRelationship addedchild of 0002166
21-04-2015 09:29vdemidovStatusnew => confirmed
22-04-2015 07:25vdemidovNote Added: 0015647
22-04-2015 07:36zedNote Added: 0015648
22-04-2015 08:04vdemidovNote Added: 0015650
22-04-2015 08:08zedNote Added: 0015652
22-04-2015 10:06vdemidovNote Added: 0015656
22-04-2015 10:42vasketsovNote Added: 0015658
22-04-2015 10:57vdemidovNote Added: 0015665
22-04-2015 11:05vasketsovNote Added: 0015668
23-04-2015 07:36vdemidovNote Added: 0015684
23-04-2015 08:17vasketsovNote Added: 0015685
23-04-2015 09:30vdemidovNote Added: 0015693
23-04-2015 15:39vasketsovNote Added: 0015708
23-04-2015 17:41vdemidovNote Added: 0015710
23-04-2015 18:48vasketsovNote Added: 0015711
23-04-2015 18:58vasketsovNote Added: 0015712
23-04-2015 21:04vdemidovNote Added: 0015714
24-04-2015 00:07vasketsovNote Added: 0015716
24-04-2015 04:26vasketsovNote Added: 0015717
24-04-2015 04:40zedNote Added: 0015719
24-04-2015 04:50zedNote Added: 0015720
24-04-2015 06:30vasketsovNote Added: 0015722
24-04-2015 07:58vdemidovNote Added: 0015723
26-04-2015 11:34vdemidovNote Added: 0015759
26-04-2015 11:50vasketsovNote Added: 0015760
26-04-2015 13:34vdemidovNote Added: 0015770
26-04-2015 13:39zedNote Added: 0015773
02-10-2015 19:51zedNote Added: 0016509
02-10-2015 20:43vdemidovNote Added: 0016511
02-10-2015 20:47zedNote Added: 0016512
02-10-2015 20:54vdemidovNote Added: 0016513
02-10-2015 21:06zedNote Added: 0016514
02-10-2015 21:09vdemidovNote Added: 0016515
04-10-2015 15:28vdemidovTarget Version151010 => 151111
09-10-2015 09:06vdemidovRelationship replacedparent of 0002166
13-10-2015 09:20vdemidovTarget Version151111 => 26xxxx
28-10-2015 20:53vdemidovRelationship addedchild of 0002875
29-10-2015 11:05vdemidovRelationship addedrelated to 0002877
30-10-2015 08:57vdemidovRelationship addedchild of 0002878
05-11-2015 07:51vdemidovNote Added: 0016732
05-11-2015 08:07vdemidovTarget Version26xxxx => 151111
05-11-2015 08:08vdemidovRelationship replacedchild of 0002166
05-11-2015 22:23vdemidovNote Added: 0016736
06-11-2015 07:42vdemidovNote Added: 0016738
07-11-2015 19:17vdemidovStatusconfirmed => resolved
07-11-2015 19:17vdemidovFixed in Version => 151111
07-11-2015 19:17vdemidovResolutionopen => fixed
07-11-2015 19:17vdemidovAssigned To => vdemidov

Notes
(0015647)
vdemidov   
22-04-2015 07:25   
Я тут подумал, похоже оптимальным вариантом будет сделать по аналогии с парсером TKmlInfoSimpleParser. То есть при загрузке инишки проверяем ее кодировку и если надо конвертируем ее в UTF8. При загрузке/сохранении параметров-строк выполняем кодирование/декодирование в UTF8. Почти все содержимое ini это ANSI и только несколько строковых параметров может содержать какие-то другие символы (история поиска, пути, шаблоны имен для меток и категорий).
(0015648)
zed   
22-04-2015 07:36   
Сохранение в ini это отдельный вопрос. Если их перевести на utf8, то старые версии их резко перестанут понимать. И это, ещё до перехода на юникодную делфи.
(0015650)
vdemidov   
22-04-2015 08:04   
Можно сменить формат основного конфига на JSON c обязательной Utf-8 в качестве кодировки :)

Еще можно попробовать, для начала, добавить детектирование наличия utf-8 строк в файле и если есть, то взводить флаг, который будет предписывать читать и сохранять строки в utf-8. В результате версии начиная с некоторой начнут понимать такие конфиги, но не будут принуждать к переходу так сказать. А после перехода на юникод, сделаем принудительное сохранение в utf-8. Но последние актуальные версии будут продолжать нормально работать с такими конфигами.
(0015652)
zed   
22-04-2015 08:08   
Вариант со сменой формата на json мне нравится. А ini можно оставить для обратной совместимости, только для чтения (если не найден json). Соответственно, и читать ini всегда как анси.
(0015656)
vdemidov   
22-04-2015 10:06   
Значит нужно делать реализацию IConfigDataWriteProvider с записью в JSON.
(0015658)
vasketsov   
22-04-2015 10:42   
>Вариант со сменой формата на json
Ну, фактически это означает, что у EXE-хи будут разные конфиги (минимум ДВА), даже если две EXE-хи собирать в одну папку. То есть как бы _при_идентичных_настройках_ проверить наличие или отсутствие бага на другой версии компилятора уже не получится.
Ну и ещё не нравится то, что редактирование его руками может быть весьма нетривиально.

>взводить флаг, который будет предписывать читать и сохранять строки в utf-8
Вот такое поведение представляется куда более разумным. Тем более что при необходимости можно будет сделать флаг, что содержимое было unicode (в т.ч. LE) и сохранять его обратно также.
Пример определения, чего за unicode подсунули (через IsTextUnicode), у меня есть в u_IocpServer.pas. Только что кстати его подправил на тему строк.
(0015665)
vdemidov   
22-04-2015 10:57   
>Ну, фактически это означает, что у EXE-хи будут разные конфиги (минимум ДВА), даже если две EXE-хи собирать в одну папку.
Не, речь о переходе текущей обычной версии на новый формат конфига. То есть когда появится юникодная версия, актуальная обычная версия уже должна пользоваться новым форматом конфига.
(0015668)
vasketsov   
22-04-2015 11:05   
Так понял. Ну тогда это надо делать заведомо до следующего релиза.
Только надо договориться, что в общие конфиги не будем совать сильно нетривиальные типы данных.
Но я бы всё же предложил ещё сильно подумать насчёт json.
У него отсутствует дуракоустойчивость.
Лишняя скобка - и ппц.
А поскольку конфиги правятся руками,...
А также конфиг сильно распухнет (либо будет нечитаемым), что приведёт к задержке чтения его с диска.
А поскольку та же фигня со всеми zmp, то время старта увеличится.
А zmp делать нечитаемыми нельзя, они в репо падают с изменениями, так что там точно будет куча пробелов всяких.
В общем, потенциально или тормознутое или неудобное решение.
(0015684)
vdemidov   
23-04-2015 07:36   
> У него отсутствует дуракоустойчивость.
Убедил. Сейчас мне кажется оптимальным добавить поддержку utf-8 в ini-шки. Остается только придумать метод переключения режима. Возможные варианты:
1. Самый простой вариант завести в ini специальный ключ и проверять его сразу при загрузке, например в секции [SYSTEM]use_utf8=1
2. Пытаться читать все строки файла и определять есть ли там utf-8 или, наоборот, точно не utf-8 строки и в зависимости от этого решать как действовать дальше.
3. Просто забить на совместимость конфигов и перейти на utf-8 принудительно.
(0015685)
vasketsov   
23-04-2015 08:17   
>читать все строки файла и определять есть ли там utf-8
Я бы предложил так:
а) при загрузке проверять на unicode и в этом случае в XE2 грузить как есть и сохранять как есть, а в 2007 молча приводить к utf-8 и сохранять потом в utf-8 (вряд ли имеет смысл сохранять в unicode в 2007);
б) если не unicode то надо проверять, что буфер содержит валидную строку utf-8 или сразу по всему буферу (Pointer и Size есть), или лениво и построчно при чтении значения, лично мне кажется проще и быстрее первый вариант сразу, и без [SYSTEM]use_utf8=1 (которую никто и никогда не будет искать и менять в файле при его сохранении в блокноте руками); соответственно если конфиг валиден в смысле utf-8, то и сохранять его в таком же формате (важно для уменьшения размера в том числе, ибо желательно конечно универсальное решение и для конфигов и для zmp;);
в) если не unicode и буфер не валидная utf-8 строка - значит Ansi, тут по желанию можно конвертнуться в utf-8 и сохраняться в нём, а можно оставить Ansi, если не будет сильно трудно.
Это в теории.
(0015693)
vdemidov   
23-04-2015 09:30   
> в XE2 грузить как есть и сохранять как есть
Как показала практика в XE2 как есть сохраняет и читает именно в Ansi и будут потери если строка не совпадают с текущей локалью.

> а) при загрузке проверять на unicode и молча приводить к utf-8 и сохранять потом в utf-8
Это можно, но только на случай создания файла нотпадом. Сам САС ни в какой из версий сохранять utf-16 не будет.
> если не unicode то надо проверять, что буфер содержит валидную строку utf-8
Вот в этом и вопрос. Как это правильно делать.

Вариант с отдельным ключом хорош тем, что он позволит не перейти ненароком на utf-8 когда пользователь этого не хочет. Пример:
1. Есть старая версия, которая не понимает utf-8 но в конфиге нет не ansi строк.
2. Запускается версия с поддержкой utf-8, решает что это валидная utf-8 строка и новые строки сохраняет в utf-8, и вводится какое-то значение которое уже не ansi
3. Запускается старая версия и говорит WTF
(0015708)
vasketsov   
23-04-2015 15:39   
>позволит не перейти ненароком на utf-8
А, ну то есть параметр будет означать запрет изменения формата? Тогда в нём есть смысл.

>сохраняет и читает именно в Ansi
Это каким способом?
TStringList.ReadFromStream/ReadFromFile?
Потому что у меня есть сомнения насчёт этого в случае ReadFile сотоварищи, которые читают буфер с диска как есть и кладут его в строку не зная её тип, и наоборот пишут всю строку как есть на диск.

>Как это правильно делать
Первая же ссылка на хабре выдаёт пример, правда для веба, что-то типа php, не силён в этом, видел только что не perl. Я про то, где ещё стёб про корейцев.
(0015710)
vdemidov   
23-04-2015 17:41   
>>сохраняет и читает именно в Ansi
>Это каким способом?
TMeminifile.Create
Там в XE2 уже есть проверка кодировки открываемого файла по существующему BOM.
А по умолчанию используется Ansi

>Первая же ссылка на хабре выдаёт пример, правда для веба, что-то типа php, не силён в этом, видел только что не perl. Я про то, где ещё стёб про корейцев.
В том то и дело что стеб. А как правильно проверить файл я не знаю. Хотя для двух десятков килобайт и такой способ прокатит.
(0015711)
vasketsov   
23-04-2015 18:48   
>стеб
Да я ж про комменты:

// Returns true if $string is valid UTF-8 and false otherwise.
 function is_utf8($string) {
 // From w3.org/International/questions/qa-forms-utf-8.html
 return preg_match('%^(?:
 [\x09\x0A\x0D\x20-\x7E] # ASCII
 | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
 | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
 | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
 | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
 | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
 )*$%xs', $string);
 } // function is_utf8


А вот ещё:
http://phpclub.ru/talk/threads/utf8-%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D0%B0-%D0%BD%D0%B0-%D0%B2%D0%B0%D0%BB%D0%B8%D0%B4%D0%BD%D0%BE%D1%81%D1%82%D1%8C.35808/

но всё php - я в нём не бельмеса.

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

Символы UTF-8 получаются из Unicode следующим образом:
Unicode UTF-8:
0x00000000 — 0x0000007F: 0xxxxxxx
0x00000080 — 0x000007FF: 110xxxxx 10xxxxxx
0x00000800 — 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
0x00010000 — 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx


Теоретически возможны, но не включены в стандарт также:
0x00200000 — 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
0x04000000 — 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx


Короче, судя по всему, это вопрос или прямого однопроходного алгоритма, или регулярного выражения.
(0015712)
vasketsov   
23-04-2015 18:58   
Но вообще я бы плясал от IsTextUnicode
https://msdn.microsoft.com/en-us/library/windows/desktop/dd318672(v=vs.85).aspx

и сотоварищи
MultiByteToWideChar
WideCharToMultiByte
возможно флаг WC_ERR_INVALID_CHARS

и результат:

The function returns 0 if it does not succeed. To get extended error information, the application can call GetLastError, which can return one of the following error codes:
•ERROR_INSUFFICIENT_BUFFER. A supplied buffer size was not large enough, or it was incorrectly set to NULL.
•ERROR_INVALID_FLAGS. The values supplied for flags were not valid.
•ERROR_INVALID_PARAMETER. Any of the parameter values was invalid.
•ERROR_NO_UNICODE_TRANSLATION. Invalid Unicode was found in a string.

последнее может быть оно и есть?
(0015714)
vdemidov   
23-04-2015 21:04   
Ну регексп это перебор. А вот IsTextUnicode уже интереснее.
Хотя мне все больше хочется повторить то что уже есть из коробки в версии XE2: проверяем BOM в том числе и для utf-8, отсутствие BOM считаем признаком ANSI, по результатам конвертим в текущий тип string.
(0015716)
vasketsov   
24-04-2015 00:07   
>отсутствие BOM считаем признаком ANSI
Ничего подобного.
Если нет BOM но залетели внутрь IsTextUnicode - значит прилетел WideString.
См. что я залил себе в парсер kml, это кусок вставки меток из буфера обмена.
Специально оставил как есть чтобы ещё немного поиграться.
Пока строка из буфера обмена забиралась как WideString - Get для неё там отлично работал. Вернул забор в виде Ansi просто потому что мы её копируем в буфер обмена как UTF-8.
(0015717)
vasketsov   
24-04-2015 04:26   
Подумалось вот ещё. Может есть софт типа тоталкомандера или нотепада какого (в смысле, на дельфе и открытый), где решена проблема распознавания буфера и кодировок, проверки utf-8? Откуда это официально можно было бы спереть. Есть мысли на эту тему?
(0015719)
zed   
24-04-2015 04:40   
Можно сделать настройку: "Файлы без BOM распозновать как:" и список кодировок. По-умолчанию выставить системную или utf-8.
(0015720)
zed   
24-04-2015 04:50   
> Есть мысли на эту тему?
Посмотри на Notepad++ (хоть там и с++) - умеет распозновать utf-8 без BOM.
(0015722)
vasketsov   
24-04-2015 06:30   
http://doublecmd.sourceforge.net/
Double Commander
использует судя по всему
Summary
Charset Detector - as the name says - is a stand alone executable module for automatic charset detection of a given text.
It can be useful for internationalisation support in multilingual applications such as web-script editors or Unicode editors.
Given input buffer will be analysed to guess used encoding. The result can be used as control parameter for charset conversation procedure.
Charset Detector can be compiled (and hopefully used) for MS Windows (as dll - dynamic link library) or Linux.
Based on Mozilla's i18n component - http://www.mozilla.org/projects/intl/.

-----------State
Version 0.2.6 stable.
The latest version can be found at http://chsdet.sourceforge.net.
(0015723)
vdemidov   
24-04-2015 07:58   
А мне кажется, что с этим можно вообще не заморачиваться. Версия скомпиленная в XE2 по-умолчанию все сохраняет в ANSI, то есть ровно так же как есть сейчас. Можно добавить в текущую версию проверку BOM и перекодировку, а со временем в юникодной версии установить принудительное сохранение в utf-8. И все будет работать в обычной версии ничуть не хуже чем сейчас, а в юникодной - лучше.
(0015759)
vdemidov   
26-04-2015 11:34   
Блин. Как лень делать работу, которая станет не нужно сразу после перехода на XE2.
(0015760)
vasketsov   
26-04-2015 11:50   
Ну тогда надо заранее приготовиться к потоку ссаных тряпок, доделать то что совсем уж не причёсано, и выпустить релиз под XE2. Тогда делать ненужную работу по переходу не придётся. Вся работа будет только нужной.
(0015770)
vdemidov   
26-04-2015 13:34   
Ну тогда давай конкретные примеры, когда юникодная версия работает хуже чем текущая.
(0015773)
zed   
26-04-2015 13:39   
Я думаю, если внезапно начать собирать ночнушку в XE2, багтрекер треснет от наплыва багов :)
(0016509)
zed   
02-10-2015 19:51   
Мне вот сейчас подумалось, что первым делом нам нужно перевести все zmp на юникод. Особенно, в свете того, что появился интерес со стороны французских товарищей и они сейчас могут захотеть писать в params.txt локализованные названия карт. И по-моему, проще всего это добавить флаг 'use_utf8' или положить файлик типа encoding.txt в zmp с актуальной кодировкой всех ini/txt.

Причём, это имеет смысл делать уже сейчас, вне зависимости от того, каким мы компилятором пользуемся.
(0016511)
vdemidov   
02-10-2015 20:43   
Просто очень лень вручную делать то, что получим автоматом при переходе на новую версию компилятора. Там детектирование на основе BOM уже есть из коробки.
(0016512)
zed   
02-10-2015 20:47   
Не факт, что мы на эту новую версию ещё перейдём. Да и детектирование по bom не такой уж и надёжный способ.
(0016513)
vdemidov   
02-10-2015 20:54   
>Не факт, что мы на эту новую версию ещё перейдём.
Это плохо.
>Да и детектирование по bom не такой уж и надёжный способ.
ИМХО он оптимален по соотношению затрачиваемых усилий на реализацию, затрачиваемых при работе ресурсов и полученному результату.
(0016514)
zed   
02-10-2015 21:06   
Я бы хотел, чтобы наш следующий релиз уже поддерживал юникод, хотя бы в params.txt.
(0016515)
vdemidov   
02-10-2015 21:09   
Я пасс. Я любую поддержку юникода буду добавлять только после перехода на юникодную делфи.
(0016732)
vdemidov   
05-11-2015 07:51   
Кажется придумал как добавить более менее вменяемую поддержку юникода в ини файлах в D2007. Делаем в Compatibility наследника TMemIniFile с таким же именем. И добавляем туда недостающее свойство Encoding, поддержку чтения файлов с юникодом и сохранение файла с использованием Encoding. В итоге все ограничится добавлением одного юнита в uses в нужных местах. Недостаток - при использовании одновременно юникодной и неюникодной версий - неюникодная версия будет портить символы не из основной локали. Но если их не будет, то все будет работать прозрачно.
(0016736)
vdemidov   
05-11-2015 22:23   
Вроде бы даже работает. Юникодные данные, конечно, теряются после открытия в неюникодной версии, но только те, которые не из текущей локали. То есть не хуже чем было, а по некоторым параметрам лучше.
(0016738)
vdemidov   
06-11-2015 07:42   
Осталось придумать по какому признаку сделать принудительное сохранение ini в utf в юникодной версии. Самый простой вариант, после выпуска релиза с поддержкой юникодных конфигов, юникодная версия будет форсировано сохранять в utf-8 или utf-16, а обычная просто будет сохранять существующую кодировку.