30 апреля 2010 г.

Работа с текстовыми файлами в любой кодировке из Delphi до 2009

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

Мир вокруг нас уже давно не ограничивается 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.

Скачать всё одним архивом. Примечание: обновлённая версия модуля Encoding с новыми функциями доступна тут.

9 комментариев :

  1. Прилично ли в русскоязычном блоге спрашивать про копирайты? :P

    ОтветитьУдалить
  2. Эммм... а что такое? :)

    Если вопрос про авторство - то это мой код, который я недавно написал в качестве ответа на вопрос Круглого Стола на DelphiKingdom.

    Если вопрос про то, можно ли это выкладывать/модифицировать - делайте что угодно. При перепубликации буду благодарен за ссылку на источник, вот и всё, пожалуй.

    ОтветитьУдалить
  3. О кул, спасибо. Как раз недавно на работе столкнулся с такой проблемой.

    ОтветитьУдалить
  4. Ну вот, чтож ты не сказал что Encoding выдран из джедая.
    Кстати ты переопределил TStringList а как делфи определит какой TStringList я имею в виду если подключано сразу два модуля: Clasess и StringListUnicodeSupport?

    ОтветитьУдалить
  5. Всё я врубилсо почитава твою статьтю "Шаманский метод Geo":

    Дело в том, что с большой степенью вероятности Вам в проектировании формы потребуются компоненты, которые определены в том же юните, где и оригинальный компонент. Чтобы использовался именно модифицированный компонент, нужно грамотно задать порядок подключаемых юнитов в разделе uses. По правилам языка если в uses есть два модуля, содержащих одно и то же имя, то будет использован элемент из того модуля, который в списке uses указан позже. Меняя порядок юнитов в uses, можно получать нужную комбинацию оригинальных и модифицированных компонент в форме. Однако все равно сохраняется ограничение, что в одной форме невозможно использовать и оригинальный компонент, и его модификацию.

    ОтветитьУдалить
  6. >>> Ну вот, чтож ты не сказал что Encoding выдран из джедая.
    С чего вы это взяли? Encoding.pas - это вынесенный в отдельный модуль класс TEncoding из Delphi 2010 с адаптацией под старые версии Delphi (D4 и выше). Джедаи не имеют к этому никакого отношения.

    ОтветитьУдалить
  7. Анонимный4 мая 2010 г., 1:39

    ну вот как бы одно из двух
    либо это ваш код, либо код Борлана(ок, ок, Эмбаркадера) допиленный вами о совместимости с D4.
    или - или.

    > Если вопрос про авторство - то это мой код

    > Нужно просто вытащить из Delphi 2010 код TEncoding

    ОтветитьУдалить
  8. >>> либо это ваш код, либо код Борлана(ок, ок, Эмбаркадера) допиленный вами о совместимости с D4
    Я же чётко сказал: Encoding.pas - это оформленный в отдельный модуль класс TEncoding из D2010.

    ОтветитьУдалить
  9. Спасибо Вам. Долго не мог найти ответ на свой вопрос, а именно: "Как загрузить Unicode файл в StringList"

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.