Обработка unicode текстового файла

Форум для изучающих FPC и их учителей.

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

Обработка unicode текстового файла

Сообщение Copycon » 11.05.2016 14:16:38

Доброго времени суток.

Когда-то программерил на паскале и вот потребовалось "вспомнить молодость".
Надо написать програмку на Pascal для обработки текстовых файлов под Windows в кодировке Unicode.
Копался в Интернете, справке и тд и ничего найти путного не смог, как-то писал, пытался и ничего толкового не получилось.

Под обычный Дос наклепал заготовку програмки за пол часа, все, что она должна делать, это выдергивать цитаты из текстового файла и писать их другой файл.
Помогите пожалуйста переделать ее под Unicode:

Код: Выделить всё
const
   Ent     = #13#10 ;
var
   FileIn,
   FileOut : Text ;
   S1, S2  : String ;
   L       : Longint ;
   W1, W2  : Word ;
   B1, B2  : Byte ;

procedure Quit (ErrCode: byte) ;
var
   Stmp    : String ;
begin
     case ErrCode of
          00 : Stmp:='Done !'+Ent ;
          01 : Stmp:='Usage: Str_Export <InFile> <OutFile>'+Ent ;
          02 : Stmp:='Error in Input File.'+Ent ;
          03 : Stmp:='Error in Output File.'+Ent ;
     else
          Stmp:='Unknown Error.'+Ent ;
     end ;
     Write(Stmp) ;
{$I-}
     Close(FileIn) ;
     IOResult ;
     Close(FileOut) ;
     IOResult ;
{$I+}
     Halt(ErrCode) ;
end ;

procedure Init ;
begin
     if ParamCount <> 2 then Quit(1) ;
     Assign(FileIn, ParamStr(1)) ;
     Assign(FileOut, ParamStr(2)) ;
{$I-}
     Reset(FileIn) ;
     if IOResult <> 0 then Quit(2) ;
     Reset(FileOut) ;
     if IOResult = 0 then Quit(3) ;
     Rewrite(FileOut) ;
     if IOResult <> 0 then Quit(3) ;
{$I+}
end ;

begin
     Init ;

     while not EoF(FileIn) do
     begin
          ReadLn(FileIn, S1) ;
          S2:='' ;
          b1:=1 ;
          repeat
               if S1[b1] = '"' then
               begin
                    b2:=b1+1 ;
                    while (S1[b2] <> '"') and (b2 <= byte(S1[0])) do
                    begin
                         S2:=S2+S1[b2] ;
                         inc(b2) ;
                    end ;
                    b1:=b2 ;
               end ;
               inc(b1) ;
          until b1 > byte(S1[0]) ;
          if S2 <> '' then WriteLn(FileOut, S2) ;
     end ;

     Quit(0) ;
end.
Copycon
незнакомец
 
Сообщения: 3
Зарегистрирован: 11.05.2016 14:08:06

Re: Обработка unicode текстового файла

Сообщение Дож » 11.05.2016 15:21:49

Чем именно программа не поддерживает юникод?
Код: Выделить всё
C:\data\temp> type unicode.txt
Юникод "ЮЮЮЮЮЮЮЮНИИИИИИИИИИИИИИКОООООД!!!" Юникод
"«I love unicode» — говорит нам Кролик"
"Три" "юникодных цитаты" "в одной строке"
А тут нифига

C:\data\temp> fpc -Mtp expquo.pas && rm -f unicode.out && expquo.exe unicode.txt unicode.out && type unicode.out
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling expquo.pas
expquo.pas(7,4) Note: Local variable "L" not used
expquo.pas(8,4) Note: Local variable "W1" not used
expquo.pas(8,8) Note: Local variable "W2" not used
Linking expquo.exe
73 lines compiled, 0.1 sec, 27280 bytes code, 1268 bytes data
3 note(s) issued
Done !
ЮЮЮЮЮЮЮЮНИИИИИИИИИИИИИИКОООООД!!!
«I love unicode» — говорит нам Кролик
Триюникодных цитатыв одной строке


Проблема может быть только в том, что использованный для хранения строки файла тип ShortString не может содержать себе более 255 байт, но это не связано напрямую с юникодом.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Обработка unicode текстового файла

Сообщение Copycon » 11.05.2016 15:47:05

А у вас это действительно unicode?!!
У меня unicode файл выглядит вот так (скриншот хекс-вьювера):

Код: Выделить всё
0000: FEFF 0023 0020 0053 │ 0054 0041 0052 0054  # START
0010: 000D 000A 004B 0049 │ 004C 004C 0041 004C  ♪◙KILLAL
0020: 004C 000D 000A 0055 │ 0053 0045 0048 0054  L♪◙USEHT
0030: 004D 004C 003D 0031 │ 000D 000A 0053 0048  ML=1♪◙SH
0040: 004F 0057 0053 0054 │ 0041 0054 0020 0030  OWSTAT 0
0050: 000D 000A 0053 0048 │ 004F 0057 004F 0042  ♪◙SHOWOB
0060: 004A 0053 0020 0030 │ 000D 000A 0024 004F  JS 0♪◙$O
0070: 004E 0047 004C 004F │ 0041 0044 003D 0022  NGLOAD="
0080: 006C 006F 0061 0064 │ 0067 0022 000D 000A  loadg"♪◙
0090: 0024 004F 004E 0047 │ 0053 0041 0056 0045  $ONGSAVE
00A0: 003D 0022 0073 0061 │ 0076 0065 0067 0022  ="saveg"
00B0: 000D 000A 0024 006F │ 006E 006F 0062 006A  ♪◙$onobj
00C0: 0073 0065 006C 0020 │ 003D 0020 0022 006F  sel = "o
00D0: 006E 006F 0062 006A │ 0073 0065 006C 0022  nobjsel"
00E0: 000D 000A 0073 0068 │ 006F 0077 0069 006E  ♪◙showin
00F0: 0070 0075 0074 0020 │ 0030 000D 000A 0064  put 0♪◙d
0100: 0065 0062 0075 0067 │ 003D 0031 000D 000A  ebug=1♪◙
0110: 000D 000A           │                      ♪◙


И, по мимо проблемы строк в 255 символов которую тоже надо решить (как подозреваю, это решается с помощью разных типов PChar, AnsiString, WideString)
Тут:
а) двухбайтовая кодировка символов - которые надо сравнивать/обрабатывать и желательно не вручную
б) перевод строк в виде "000D 000A" - который меня просто УБИВАЕТ! И который не обрабатывается нормально стандартными функциями ReadLn и WriteLn.
я докопался в документации до константы LineEnding и процедуры SetTextLineEnding( var f: Text; Ending: string );
но, заставить это работать у меня не получилось.
Copycon
незнакомец
 
Сообщения: 3
Зарегистрирован: 11.05.2016 14:08:06

Re: Обработка unicode текстового файла

Сообщение Дож » 11.05.2016 16:58:55

А у вас это действительно unicode?!!

Да:
Код: Выделить всё
C:\data\temp\unicode.txt
0000000000: D0 AE D0 BD D0 B8 D0 BA │ D0 BE D0 B4 20 22 D0 AE  Ю›н›и›к›о›д› "Ю›
0000000010: D0 AE D0 AE D0 AE D0 AE │ D0 AE D0 AE D0 AE D0 9D  Ю›Ю›Ю›Ю›Ю›Ю›Ю›Н›
0000000020: D0 98 D0 98 D0 98 D0 98 │ D0 98 D0 98 D0 98 D0 98  И›И›И›И›И›И›И›И›
0000000030: D0 98 D0 98 D0 98 D0 98 │ D0 98 D0 98 D0 9A D0 9E  И›И›И›И›И›И›К›О›
0000000040: D0 9E D0 9E D0 9E D0 9E │ D0 94 21 21 21 22 20 D0  О›О›О›О›Д›!!!" Ю
0000000050: AE D0 BD D0 B8 D0 BA D0 │ BE D0 B4 0D 0A 22 C2 AB  ›н›и›к›о›д›♪◙"«›
0000000060: 49 20 6C 6F 76 65 20 75 │ 6E 69 63 6F 64 65 C2 BB  I love unicode»›
0000000070: 20 E2 80 94 20 D0 B3 D0 │ BE D0 B2 D0 BE D1 80 D0   —›› г›о›в›о›р›и
0000000080: B8 D1 82 20 D0 BD D0 B0 │ D0 BC 20 D0 9A D1 80 D0  ›т› н›а›м› К›р›о
0000000090: BE D0 BB D0 B8 D0 BA 22 │ 0D 0A 22 D0 A2 D1 80 D0  ›л›и›к›"♪◙"Т›р›и
00000000A0: B8 22 20 22 D1 8E D0 BD │ D0 B8 D0 BA D0 BE D0 B4  ›" "ю›н›и›к›о›д›
00000000B0: D0 BD D1 8B D1 85 20 D1 │ 86 D0 B8 D1 82 D0 B0 D1  н›ы›х› ц›и›т›а›т
00000000C0: 82 D1 8B 22 20 22 D0 B2 │ 20 D0 BE D0 B4 D0 BD D0  ›ы›" "в› о›д›н›о
00000000D0: BE D0 B9 20 D1 81 D1 82 │ D1 80 D0 BE D0 BA D0 B5  ›й› с›т›р›о›к›е›
00000000E0: 22 0D 0A D0 90 20 D1 82 │ D1 83 D1 82 20 D0 BD D0  "♪◙А› т›у›т› н›и
00000000F0: B8 D1 84 D0 B8 D0 B3 D0 │ B0 0D 0A                 ›ф›и›г›а›♪◙


а) двухбайтовая кодировка символов - которые надо сравнивать/обрабатывать и желательно не вручную

Вы хотите обрабатывать utf-8 или utf-16 файлы? Под словом «юникод» обычно подразумевают кодировку utf-8

б) перевод строк в виде "000D 000A" - который меня просто УБИВАЕТ! И который не обрабатывается нормально стандартными функциями ReadLn и WriteLn.


Это уже что-то :)

Проблема в том, что юникод — слишком обширная тема, и я не готов (да и не смогу) в коротком форумном посте рассказать все тонкости и нюансы его использования на все случаи жизни.

Конкретно под данную задачу программу можно написать так:
Код: Выделить всё
{$MODE OBJFPC}
{$H+}
{$PACKENUM 1}
{$codepage UTF8}

{$IFNDEF windows} // Ну а вдруг?
uses
  cwstring;
{$ENDIF}

var
  Line: UnicodeString;
  C: UnicodeChar;
  Mode: Boolean;

begin
  if ParamStr(1) <> '' then begin
    Assign(input, ParamStr(1));
    Reset(input);
  end;
  if ParamStr(2) <> '' then begin
    Assign(output, ParamStr(2));
    Rewrite(output);
  end;
  while not Eof do begin
    Readln(Line);
    Mode := False;
    for C in Line do
      if C <> '"' then begin
        if Mode then begin
          Write(C);
        end;
      end else
        Mode := not Mode;
    Writeln(' END_OF_LINE_MARKER'); // просто для проверки, что две строки файла не прочлись как одна
  end;
end.


Я специально подготовил входной файл со всеми тремя основными типами концов строк
Код: Выделить всё
"ЮНИКОД!"
"Кролик!"
"АЗАЗИЙ"
"«»—" "и т.д" "и т.п."

и его концы строк выровнены в дампе по правому краю для того, чтобы их было проще увидеть:
Код: Выделить всё
C:\data\temp\unicode.txt
0000000000: 22 D0 AE D0 9D D0 98 D0 │ 9A D0 9E D0 94 21 22 0D  "Ю›Н›И›К›О›Д›!"♪
0000000010: 22 D0 9A D1 80 D0 BE D0 │ BB D0 B8 D0 BA 21 22 0A  "К›р›о›л›и›к›!"◙
0000000020: 22 D0 90 D0 97 D0 90 D0 │ 97 D0 98 D0 99 22 0D 0A  "А›З›А›З›И›Й›"♪◙
0000000030: 22 C2 AB C2 BB E2 80 94 │ 22 20 22 D0 B8 20 D1 82  "«›»›—››" "и› т›
0000000040: 2E D0 B4 22 20 22 D0 B8 │ 20 D1 82 2E D0 BF 2E 22  .д›" "и› т›.п›."
0000000050: 0A                      │                          ◙


Запуск программы на данном файле:
Код: Выделить всё
C:\data\temp> fpc -gl q.pas && rm -f unicode.out && q.exe unicode.txt unicode.out && type unicode.out
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling q.pas
Linking q.exe
37 lines compiled, 0.2 sec, 36416 bytes code, 1316 bytes data
ЮНИКОД! END_OF_LINE_MARKER
Кролик! END_OF_LINE_MARKER
АЗАЗИЙ END_OF_LINE_MARKER
«»—и т.ди т.п. END_OF_LINE_MARKER
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Обработка unicode текстового файла

Сообщение Copycon » 11.05.2016 17:25:49

К сожалению, приведенная программа проблему перевода строки в виде "000D 000A" - не решает.
и да, похоже, что у меня файлы именно "utf-16"
Copycon
незнакомец
 
Сообщения: 3
Зарегистрирован: 11.05.2016 14:08:06

Re: Обработка unicode текстового файла

Сообщение Дож » 12.05.2016 14:16:42

и да, похоже, что у меня файлы именно "utf-16"

С этого надо было начинать :)

Насколько могу судить, стандартный Readln сейчас не умеет читать utf-16, нужно использовать какую-нибудь либу это поддерживающую, сконвертировать исходный файл в utf-8 и читать уже его (мне кажется этот вариант самым удобным, можно это сделать, например, при помощи проги iconv), либо аккуратно написать свою читалку utf-16.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47


Вернуться в Обучение Free Pascal

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

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6

Рейтинг@Mail.ru