Мир вокруг нас уже давно не ограничивается ANSI, а уж тем более ASCII. На фоне этого ваши древние ANSI-программы выглядят не очень-то хорошо. Потому что они молчаливо игнорируют существование альтернативных кодировок вообще. Для них существует только текущая кодовая страница ANSI, не больше и не меньше.
Хотя самое нормальное решение включает в себя переход на Delphi 2010, часто вас и пользователей устраивает ваше ANSI-приложение, если бы... оно работало бы с любыми текстовыми файлами. Как это достигается? Ну, в начале текстового файла вставляется метка BOM (Byte Order Mask), указывающая на кодировку файла. Это означает, что если вы хотите загрузить произвольный текстовый файл, то вам нужно прочитать несколько байт в начале файла, после чего определить формат файла и преобразовать его в нужный вам - это довольно много работы, не так ли?
Как это реализуется в Delphi? В Delphi 2009 и выше у вас появляется класс TEncoding, позволяющий работать с различными кодировками. Класс TStrings (и TStringList) используют TEncoding для определения кодировки файла и всех преобразований.
Было бы неплохо заиметь такую штуку, скажем в Delphi 7 или Delphi 2007? К счастью, это очень просто сделать (эй, это заняло примерно 8 минут моего времени, включая проверку). Нужно просто вытащить из Delphi 2010 код TEncoding и новые методы LoadFromFile(Stream)/SaveToFile(Stream).
Представляю вашему вниманию два модуля: Encoding.pas - здесь сидит новый класс TEncoding - штука, полезная сама по себе, даже если вы не используете её для работы с текстовыми файлами.
Второй модуль, StringListUnicodeSupport.pas методом Geo добавляет в обычный TStringList поддержку произвольной кодировки, а также перегруженные варианты методов загрузки и сохранения, позволяющие указать кодировку явно (SaveToFile/Stream сохраняют в ANSI, если вам нужна другая кодировка, вы должны указать её вторым параметром).
Вам достаточно подключить StringListUnicodeSupport в uses и вы волшебным образом получаете возможность работы с любыми текстовыми файлами:
uses StringListUnicodeSupport; procedure TForm1.Button1Click(Sender: TObject); var Str: TStringList; // использует новый TStringList из StringListUnicodeSupport begin Str := TStringList.Create; try Str.LoadFromFile('C:\utf8_encoded_File.txt'); // покажет содержимое файла вместо дракозябров (если это возможно в текущей кодовой странице ANSI) Memo1.Lines.Assign(Str); // Примечание: прямой вызов Memo1.Lines.LoadFromFile работать не будет, потому что там не используется новый TStringList finally FreeAndNil(Str); end; end;Как пользователи динозаврических Delphi вы, вероятно, не знакомы с TEncoding и перегруженным вариантом методов TStrings. Что ж, к счастью, вы можете воспользоваться online-справкой: TEncoding, использование TEncoging, LoadFromFile, SaveToFile.
Скачать всё одним архивом.
Прилично ли в русскоязычном блоге спрашивать про копирайты? :P
ОтветитьУдалитьЭммм... а что такое? :)
ОтветитьУдалитьЕсли вопрос про авторство - то это мой код, который я недавно написал в качестве ответа на вопрос Круглого Стола на DelphiKingdom.
Если вопрос про то, можно ли это выкладывать/модифицировать - делайте что угодно. При перепубликации буду благодарен за ссылку на источник, вот и всё, пожалуй.
О кул, спасибо. Как раз недавно на работе столкнулся с такой проблемой.
ОтветитьУдалитьНу вот, чтож ты не сказал что Encoding выдран из джедая.
ОтветитьУдалитьКстати ты переопределил TStringList а как делфи определит какой TStringList я имею в виду если подключано сразу два модуля: Clasess и StringListUnicodeSupport?
Всё я врубилсо почитава твою статьтю "Шаманский метод Geo":
ОтветитьУдалитьДело в том, что с большой степенью вероятности Вам в проектировании формы потребуются компоненты, которые определены в том же юните, где и оригинальный компонент. Чтобы использовался именно модифицированный компонент, нужно грамотно задать порядок подключаемых юнитов в разделе uses. По правилам языка если в uses есть два модуля, содержащих одно и то же имя, то будет использован элемент из того модуля, который в списке uses указан позже. Меняя порядок юнитов в uses, можно получать нужную комбинацию оригинальных и модифицированных компонент в форме. Однако все равно сохраняется ограничение, что в одной форме невозможно использовать и оригинальный компонент, и его модификацию.
>>> Ну вот, чтож ты не сказал что Encoding выдран из джедая.
ОтветитьУдалитьС чего вы это взяли? Encoding.pas - это вынесенный в отдельный модуль класс TEncoding из Delphi 2010 с адаптацией под старые версии Delphi (D4 и выше). Джедаи не имеют к этому никакого отношения.
ну вот как бы одно из двух
ОтветитьУдалитьлибо это ваш код, либо код Борлана(ок, ок, Эмбаркадера) допиленный вами о совместимости с D4.
или - или.
> Если вопрос про авторство - то это мой код
> Нужно просто вытащить из Delphi 2010 код TEncoding
>>> либо это ваш код, либо код Борлана(ок, ок, Эмбаркадера) допиленный вами о совместимости с D4
ОтветитьУдалитьЯ же чётко сказал: Encoding.pas - это оформленный в отдельный модуль класс TEncoding из D2010.
Спасибо Вам. Долго не мог найти ответ на свой вопрос, а именно: "Как загрузить Unicode файл в StringList"
ОтветитьУдалитьДоброго времени суток. Столкнулся с проблемой кодировки текстовых файлов. Прочел Вашу статью. Заинтересовало. В какие директории Делфи 7 следует поместить указанные модули так чтобы заработало?
ОтветитьУдалитьОтлично, я думал уже переводить проект на новую версию Delphi как раз из-за этого.
ОтветитьУдалитьПорывшись в исходниках не нашел где мне взять информацию, какою именно кодировку я загрузил? не подскажите?
Не очень понятно зачем это надо, если в ANSI-версиях Delphi в строке может лежать только одна кодировка: ACP. Но если сильно хотите, то можно просто скопировать код StringListUnicodeSupport.TStringList.LoadFromStream с минимальными изменениями. В том коде у вас на руках будет Encoding - экземпляр TEncoding. Ну а его класс можно сравнить с известными: TUTF8Encoding, например.
УдалитьХорошая статья, спасибо. Может подскажите как можно конвертировать StringList в нужную кодировку с уже загруженным текстом?
ОтветитьУдалитьКто вам справку-то мешает открыть?
УдалитьПолучается, что чтобы перекодировать, то нужно все выгрузить в поток, а затем заново из потока загрузиться?
УдалитьStringList.SaveToStream(mysream, TEncoding.UTF8);
StringList.LoadFromStream(mysream);
Других вариантов нет?
Есть, конечно. Я просто задачи не вижу. Вам в памяти надо StringList из одной кодировки перегнать в другую? И всё это в старых ANSI Delphi? А из какой кодировки в какую?
УдалитьДа именно в памяти, уже после загрузки. А перекодировать нужно из любой в любую, ну конечно те которые допускает TEncoding
УдалитьDelphi7
УдалитьЭто прикол такой? Вы понимаете, что в старых ANSI версиях Delphi строки не могут хранить данные в любых кодировках? Это же однобайтовые ANSI строки. Если только как бесмысленный набор байт.
УдалитьЭто я тоже не совсем понял, но как тогда я без проблем отрываю и просматриваю без абракадабры файлы в Unicode?
УдалитьВ ANSI строках старых версий Delphi может находится лишь одна кодировка - ACP, которая настраивается системой ("язык для не-Unicode программ"). В "русской" Windows она равна Win-1251. Соответственно, никакими усилиями японские иероглифы (к примеру) в неё вы не запишете.
УдалитьКогда вы загружаете текст из Unicode: если в нём лежат русские буквы - они без проблем преобразуются в Win-1251 без потерь. Но если там лежат символы вне Win-1251 (западно-европейская латиница, иероглифы, хинди и др.) - эти символы нельзя преобразовать в Win-1251, поэтому они будут заменены вопросиками.
Поэтому мне не очень понятна задача преобразования из одной ANSI-кодировки в произвольную другую. Если только целевая кодировка не OEM и не Unicode, то такое преобразование не имеет смысла - ведь общих символов у двух разных ANSI кодировок просто нет. Вам необходима поддержка Unicode, если вы хотите работать с несколькими разными кодировками. В старых Delphi вы можете это делать, но не стандартным кодом RTL: вам нужно использовать WideString (Unicode строки) вместо String (ANSI строки), а также заменить все классы (TStringList) и весь код с String на WideString. Извращение, но можно. Но проще перейти на новую Delphi с родной поддержкой Unicode.
Спасибо теперь я все понял.
ОтветитьУдалитьНе определяет кодировку utf-8 without BOM
ОтветитьУдалитьТак и не должен. BOM же нет, как он кодировку определит? Словарей нет, таблиц эвристики нет.
УдалитьПри сохранение все вроде хорошо конвертируется и сохраняется, к сожалению, кроме:
ОтветитьУдалитьStringList.SaveToFile(FileName, TEncoding.BigEndianUnicode); вылетает AV (
Поставил Delphi 10.2 Starter там такого бага нет. Даже не викидывал из uses StringListUnicodeSupport, хотя он так как-бы и ненужен
ОтветитьУдалить