типизированный файл, созданный в WIndows

Вопросы программирования и использования среды Lazarus.

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

типизированный файл, созданный в WIndows

Сообщение Валентина » 06.09.2013 09:35:06

Продолжаю биться с переводом дельфевой программы (там все работает нормально). Столкнулась с проблемой чтения типизированного файла. В переменную типа TRec в поля какая-то фигня читается... Подскажите, пожалуйста, как бороться.
Валентина
новенький
 
Сообщения: 16
Зарегистрирован: 02.09.2013 13:05:37

Re: типизированный файл, созданный в WIndows

Сообщение bormant » 06.09.2013 09:49:53

Желательно привести тут декларацию TRec, sizeof(TRec) в программе на Delphi и FPC.
Часто подобные проблемы бывают вызваны разным выравниванием полей записи в памяти (у полей получаются разные смещения от начала записи).
http://www.freepascal.org/docs-html/pro ... 50001.1.60

Другой класс ошибок -- прямое чтение little-endian данных на big-endian или mid-endian платформах и наоборот.
Аватара пользователя
bormant
постоялец
 
Сообщения: 408
Зарегистрирован: 21.03.2012 11:26:01

Re: типизированный файл, созданный в WIndows

Сообщение Валентина » 06.09.2013 10:40:14

bormant писал(а):Желательно привести тут декларацию TRec, sizeof(TRec) в программе на Delphi и FPC.
Часто подобные проблемы бывают вызваны разным выравниванием полей записи в памяти (у полей получаются разные смещения от начала записи).
http://www.freepascal.org/docs-html/pro ... 50001.1.60

Другой класс ошибок -- прямое чтение little-endian данных на big-endian или mid-endian платформах и наоборот.


Код: Выделить всё
type
  TRec=record
  rn:string[40];
  dn:string[20];
  nazv:string[200];
  vid:string[200];
  kontr:string[150];
  krat:string[30];
  tema:string[40];
  fin:string[10];
end;
...
var
  R:TRec;
  V:File of TRec;
  dog:array[1..10000,1..15] of string;
...
begin
...   
    read(V,R);
    dog[step_txt,1]:=R.rn;
    dog[step_txt,2]:=R.dn;
    dog[step_txt,3]:=R.nazv;
    dog[step_txt,4]:=R.vid;
    dog[step_txt,5]:=R.kontr;
    dog[step_txt,6]:=R.krat;
    dog[step_txt,7]:=R.tema;
    dog[step_txt,8]:='0';
Валентина
новенький
 
Сообщения: 16
Зарегистрирован: 02.09.2013 13:05:37

Re: типизированный файл, созданный в WIndows

Сообщение bormant » 06.09.2013 11:50:38

... и ещё хотелось бы увидеть файл данных, сформированный программой на Delphi (кстати, какой версии), первые 3-4 записи или первые пару-тройку килобайт.
А ещё лучше, файл test.dat, сформированный вот этой программой, скомпилированной в Delphi:
Код: Выделить всё
type
  TRec=record
    rn: string[40];
    dn: string[20];
    nazv: string[200];
    vid: string[200];
    kontr: string[150];
    krat: string[30];
    tema: string[40];
    fin: string[10];
  end;

var
  R: TRec;
  F: file of TRec;

begin
  with R do begin
    rn := 'rn'; dn := 'dn'; nazv := 'nazv'; vid := 'vid';
    kontr := 'kontr'; krat := 'krat'; tema := 'tema'; fin := 'fin';
  end;
  Assign(F, 'test.dat'); Rewrite(F);
  Write(F, R); Write(F, R);
  Close(F);
end.


Третий вариант причины получения "фигни" -- несоответствие кодировок в том, что пишет (скорее всего Windows-1251) и в том, что читает и показывает. Если файл формировался в графическом приложении Delphi под Windows, скорее всего кодировка полей Windows-1251. Если попытаться прочитать и вывести данные консольным приложением той же Delphi, увидим "корявки", поскольку по умолчанию в консоли используется 866 кодовая страница. Если то же самое проделать в Linux, внешний вид "корявок" будет зависеть от используемой кодовой страницы (чаще всего utf8 или koi8-r, реже iso-8859-5). В этом случае достаточно перекодировать данные в используемую локалью кодировку.
Пример того, как выглядит одна и та же строка 'абвгдежзийклмнопрстуфхцчшщъыьэюя' в разных кодировках, 1251, 866, koi8-r, iso-8859-5, ucs2/utf-16, utf-8:
Код: Выделить всё
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t cp1251 | hexdump -C
00000000  e0 e1 e2 e3 e4 e5 e6 e7  e8 e9 ea eb ec ed ee ef  |................|
00000010  f0 f1 f2 f3 f4 f5 f6 f7  f8 f9 fa fb fc fd fe ff  |................|
00000020
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t cp866 | hexdump -C
00000000  a0 a1 a2 a3 a4 a5 a6 a7  a8 a9 aa ab ac ad ae af  |................|
00000010  e0 e1 e2 e3 e4 e5 e6 e7  e8 e9 ea eb ec ed ee ef  |................|
00000020
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t koi8-r | hexdump -C
00000000  c1 c2 d7 c7 c4 c5 d6 da  c9 ca cb cc cd ce cf d0  |................|
00000010  d2 d3 d4 d5 c6 c8 c3 de  db dd df d9 d8 dc c0 d1  |................|
00000020
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t iso-8859-5 | hexdump -C
00000000  d0 d1 d2 d3 d4 d5 d6 d7  d8 d9 da db dc dd de df  |................|
00000010  e0 e1 e2 e3 e4 e5 e6 e7  e8 e9 ea eb ec ed ee ef  |................|
00000020
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t ucs2 | hexdump -C
00000000  30 04 31 04 32 04 33 04  34 04 35 04 36 04 37 04  |0.1.2.3.4.5.6.7.|
00000010  38 04 39 04 3a 04 3b 04  3c 04 3d 04 3e 04 3f 04  |8.9.:.;.<.=.>.?.|
00000020  40 04 41 04 42 04 43 04  44 04 45 04 46 04 47 04  |@.A.B.C.D.E.F.G.|
00000030  48 04 49 04 4a 04 4b 04  4c 04 4d 04 4e 04 4f 04  |H.I.J.K.L.M.N.O.|
00000040
$ echo -n 'абвгдежзийклмнопрстуфхцчшщъыьэюя' | iconv -t utf8 | hexdump -C
00000000  d0 b0 d0 b1 d0 b2 d0 b3  d0 b4 d0 b5 d0 b6 d0 b7  |................|
00000010  d0 b8 d0 b9 d0 ba d0 bb  d0 bc d0 bd d0 be d0 bf  |................|
00000020  d1 80 d1 81 d1 82 d1 83  d1 84 d1 85 d1 86 d1 87  |................|
00000030  d1 88 d1 89 d1 8a d1 8b  d1 8c d1 8d d1 8e d1 8f  |................|
00000040
Последний раз редактировалось bormant 06.09.2013 13:06:44, всего редактировалось 1 раз.
Аватара пользователя
bormant
постоялец
 
Сообщения: 408
Зарегистрирован: 21.03.2012 11:26:01

Re: типизированный файл, созданный в WIndows

Сообщение Vapaamies » 06.09.2013 13:02:18

Валентина писал(а):
Код: Выделить всё
type
  TRec=record

Мне кажется, что записи, предназначенные для сохранения в файл, должны объявляться как packed record.
Аватара пользователя
Vapaamies
постоялец
 
Сообщения: 292
Зарегистрирован: 24.07.2012 22:37:59
Откуда: Санкт-Петербург

Re: типизированный файл, созданный в WIndows

Сообщение bormant » 06.09.2013 13:04:47

Vapaamies писал(а):должны объявляться как packed record
Это всего лишь один из возможных вариантов борьбы с неправильным выравниванием. Но есть и другие. И, скорее всего, в данном случае проблема не с выравниванием, а с кодировками.
Аватара пользователя
bormant
постоялец
 
Сообщения: 408
Зарегистрирован: 21.03.2012 11:26:01

Re: типизированный файл, созданный в WIndows

Сообщение Валентина » 11.09.2013 09:06:29

Сформированный файл с двумя строчками выглядит так:
113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на поставку тепловой энергии lф ;(@ х J@ ф хїC эїC Фх М‚bФх / †    њ  АЄЂї
 м= ˜х Ш»C c8E Фх Фх М‚b?°х c8E ц ц МПоставка тепловой энергии yлB »sД
b+dE мф BdE х °gE TdE ЎоA ¤
bqлB yлB »sД
b+dE х BdE JdE ёх TdE х њх »s»sЂOb4х
„B + Р @ц `х ‡УwД + Р @ц »sН«єЬ њх »sИхОАО "Саратовэнерго" я‡Уw+ pѕ­ „ѕ­    |х Р њч ”Цw€Уwяяяяя‡УwC·Уw »sД + Р @ц „ѕ­  PД­ + Саратовх $ц «·Уwpѕ­ Ё±­ Р Оказание услуг РХw (ч ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на поставку тепловой энергии lф ;(@ х J@ ф хїC эїC Фх М‚bФх / †    њ  АЄЂї
 м= ˜х Ш»C c8E Фх Фх М‚b?°х c8E ц ц МПоставка тепловой энергии yлB »sД
b+dE мф BdE х °gE TdE ЎоA ¤
bqлB yлB »sД
b+dE х BdE JdE ёх TdE х њх »s»sЂOb4х
„B + Р @ц `х ‡УwД + Р @ц »sН«єЬ њх »sИхОАО "Саратовэнерго" я‡Уw+ pѕ­ „ѕ­    |х Р њч ”Цw€Уwяяяяя‡УwC·Уw »sД + Р @ц „ѕ­  PД­ + Саратовх $ц «·Уwpѕ­ Ё±­ Р Оказание услуг РХw (ч ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на поставку тепловой энергии lф ;(@ х J@ ф хїC эїC Фх М‚bФх / †    њ  АЄЂї
 м= ˜х Ш»C c8E Фх Фх М‚b?°х c8E ц ц МПоставка тепловой энергии yлB »sД
b+dE мф BdE х °gE TdE ЎоA ¤
bqлB yлB »sД
b+dE х BdE JdE ёх TdE х њх »s»sЂOb4х
„B + Р @ц `х ‡УwД + Р @ц »sН«єЬ њх »sИхОАО "Саратовэнерго" я‡Уw+ pѕ­ „ѕ­    |х Р њч ”Цw€Уwяяяяя‡УwC·Уw »sД + Р @ц „ѕ­  PД­ + Саратовх $ц «·Уwpѕ­ Ё±­ Р Оказание услуг РХw (ч ѕxФw6XсwF,P расходныйw

Если кодировка файла ANSI, то поля выводятся на экран в виде перечеркнутых квадратиков. Если кодировка UTF8, то первое поле, например, выглядит так:
 ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на по
Валентина
новенький
 
Сообщения: 16
Зарегистрирован: 02.09.2013 13:05:37

Re: типизированный файл, созданный в WIndows

Сообщение bormant » 11.09.2013 17:53:37

Нет, нет, так дело не пойдёт. Нужен именно файл, а не его воспроизведение в тексте сообщения форума. В Стандартной форме редактирования сообщения есть возможность приложить файл. Либо его можно выложить на какой-нибудь файлообменник.
Если смущает наличие мусора между строками -- можете в общем случае не волноваться, он ни на что не должен влиять. Почему? Поясню. Запись:
Код: Выделить всё
  TRec=record
    rn: string[40];
    dn: string[20];
    nazv: string[200];
    vid: string[200];
    kontr: string[150];
    krat: string[30];
    tema: string[40];
    fin: string[10];
  end;
состоит из коротких строк, которые хранятся в виде (один байт, актуальная длина строки), (n байт в соответствии с декларацией). В первой записи
- поле rn, string[40], очевидно, равно '113-11A', которое хранится как байт с кодом 7, затем 7 символов актуального значения '113-11A', затем 33 (40-7) символов мусора, который находился в памяти на момент записи переменной в файл;
- поле rn, string[20], очевидно, равно '01.02.2011', которое хранится как байт с кодом 10 (а это, кстати, перевод строки, потому видим наше значение на 2 строке), затем 10 символов актуального значения '01.02.2011', затем 10 (20-10) символов мусора, который находился в памяти на момент записи переменной в файл;
- и так далее.
Если имелся ввиду вот этот мусор -- заполнитель строк, то чтобы его не допускать, можно перед заполнением значений и записью в файл очищать значение переменной, что-то вроде:
Код: Выделить всё
var
  R: TRec;
...
  FillChar(R, SizeOf(R), #0);
  R.rn := '113-11А';
  R.dn := '01.02.2011';
  ...
  Write(F, R);

Если же нужно было получить обычный текстовый файл, то что-то вроде:
Код: Выделить всё
type ...
var
  R: TRec;
  F: file of TRec;
begin
  Assign(F, 'file.dat'); Reset(F);
  while not EOF(F) do with R do begin
    Read(R);
    WriteLn(rn);
    WriteLn(dn);
    ...
  end;
  Close(F);
end.

А чтобы получить копию без мусора, что-то вроде:
Код: Выделить всё
type
  TRec=record
    rn: string[40];
    dn: string[20];
    nazv: string[200];
    vid: string[200];
    kontr: string[150];
    krat: string[30];
    tema: string[40];
    fin: string[10];
  end;

var
  R, R2: TRec;
  F, F2: file of TRec;

begin
  Assign(F, 'file.dat'); Reset(F);
  Assign(F2, 'copy.dat'); Rewrite(F2);
  while not EOF(F) do with R2 do begin
    Read(F, R);
    FillChar(R2, SizeOf(R2), #0);
    rn := R.rn;
    dn := R.dn;
    nazv := R.nazv;
    vid := R.vid;
    kontr := R.contr;
    krat := R.krat;
    tema := R.tema;
    fin := R.fin;
    Write(F2, R2);
  end;
  Close(F2); Close(F);
end.
Непечатные символы в начале каждого поля -- байты длинн строк -- от этого не пропадут, и визуальное разделение на строки/записи не появится.
Аватара пользователя
bormant
постоялец
 
Сообщения: 408
Зарегистрирован: 21.03.2012 11:26:01

Re: типизированный файл, созданный в WIndows

Сообщение Валентина » 12.09.2013 13:32:08

Спасибо за внимание к моей проблеме. Высылаю файлик.
"Фигня", на которую я сетовала, выглядит так в первом поле:

 ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на по

вместо

113-11А
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Валентина
новенький
 
Сообщения: 16
Зарегистрирован: 02.09.2013 13:05:37

Re: типизированный файл, созданный в WIndows

Сообщение bormant » 14.09.2013 13:13:50

Угадать, что вы сделали с файлом, несложно: открыли в текстовом редакторе и "сохранили как" в кодировке Unicode (UTF-8), ожидая, что этого будет достаточно. Однако это не так, и вот почему.
Прежде всего, это действие некорректно само по себе -- хотя файл и содержит вроде бы текстовые данные (string), но на самом деле является двоичным, поскольку наверняка содержит символы вне текстового диапазона (байт длины строки), с которыми текстовые редакторы могут обходиться весьма произвольно.
Другая ошибка связана с добавлением текстовым редактором в начало файла BOM -- byte order mark -- метки порядка байт, которая для utf-8 занимает 3 байта (EF BB BF), что привело к сдвигу остальной информации. Это важно поскольку каждая запись в файле занимает ровно 698(10) = 2BA(16) байт.
Третья ошибка аналогична второй, но касается символов с кодами больше 127 (символов за пределами ASCII). Дело в том, что буквы русского алфавита в UTF-8 кодируются 2 байтами, вместо 1 в cp1251, что вызвало дополнительное смещение данных.

На вашем месте я оставил бы хранение данных в файле cp1251, но предусмотрел преобразование считанного в кодировку локали перед выводом и преобразование из кодировки локали в cp1251 перед записью в файл -- этим будет обеспечена совместимость с программой на Delphi по хранимым данным. Для полей ввода в utf-8 нужно предусматривать в памяти вдвое больше места (1 русская буква занимает 2 байта) и контролировать допустимую длину (если в такое длинное поле "набить" цифр, то результат преобразования в cp1251 в вдвое меньшее поле не влезет).
Это не единственный возможный вариант, как именно поступить, решать вам.

В присланном файле 3 одинаковых записи:
Код: Выделить всё
     rn:113-11А
     dn:01.02.2011
   nazv:Договор на поставку тепловой энергии
    vid:Поставка тепловой энергии
  kontr:ОАО "Саратовэнерго"
   krat:Саратов
   tema:Оказание услуг
    fin:расходный
и запись по смещению 0 -- пустая. Результат получен обратным преобразованием в 1251 и отрезанием лишнего в начале.

Если хранить в utf-8, придётся пересмотреть размеры полей, например, "расходный" уже не вместится.

Во вложении пара файлов, в 1251 и utf8.

Добавлено спустя 23 минуты 13 секунд:
Пример с конвертацией при выводе (функция преобразования наколенная, посмотрите наличие более эффективных вариантов):
Код: Выделить всё
type
  TRec=record
    rn: string[40];
    dn: string[20];
    nazv: string[200];
    vid: string[200];
    kontr: string[150];
    krat: string[30];
    tema: string[40];
    fin: string[10];
  end;

function Str2Utf8(S: String): string;
type
  str3: string[3];
  function Char1251toUtf8(C: Char): str3; inline;
  begin
    case Byte(C) of
      192..239: Char1251toUtf8 := #208 + Char(Byte(C) - 48);
      240..255: Char1251toUtf8 := #209 + Char(Byte(C) - 112);
      else      Char1251toUtf8 := C;
    end;
  end;
var
  i: Integer;
  r: String;
begin
  r := '';
  for i := 1 to Length(S) do
    r := r + Char1251toUtf8(S[i]);
  Str2Utf8 := r;
end;

var
  R: TRec;
  F: file of TRec;

procedure DumpRecInUtf8(const R: TRec);
begin
  WriteLn('rn:':6, Str2Utf8(rn));
  WriteLn('dn:':6, Str2Utf8(dn));
  WriteLn('nazv:':6, Str2Utf8(nazv));
  WriteLn('vid:':6, Str2Utf8(vid));
  WriteLn('kontr:':6, Str2Utf8(kontr));
  WriteLn('krat:':6, Str2Utf8(krat));
  WriteLn('tema:':6, Str2Utf8(tema));
  WriteLn('fin:':6, Str2Utf8(fin));
  WriteLn;
end;

begin
  Assign(F, 'dog1251.dat'); Reset(F);
  while not EOF(F) do begin
    Read(F, R);
    DumpRecInUtf8(R);
  end;
  Close(F);
end.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя
bormant
постоялец
 
Сообщения: 408
Зарегистрирован: 21.03.2012 11:26:01

Re: типизированный файл, созданный в WIndows

Сообщение Валентина » 19.09.2013 08:34:18

Большое спасибо за помощь, все получилось! Этот этап прошла, воюю дальше...
Валентина
новенький
 
Сообщения: 16
Зарегистрирован: 02.09.2013 13:05:37


Вернуться в Lazarus

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

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

Рейтинг@Mail.ru