типизированный файл, созданный в WIndows
Модератор: Модераторы
типизированный файл, созданный в WIndows
Продолжаю биться с переводом дельфевой программы (там все работает нормально). Столкнулась с проблемой чтения типизированного файла. В переменную типа TRec в поля какая-то фигня читается... Подскажите, пожалуйста, как бороться.
Желательно привести тут декларацию TRec, sizeof(TRec) в программе на Delphi и FPC.
Часто подобные проблемы бывают вызваны разным выравниванием полей записи в памяти (у полей получаются разные смещения от начала записи).
http://www.freepascal.org/docs-html/pro ... 50001.1.60
Другой класс ошибок -- прямое чтение little-endian данных на big-endian или mid-endian платформах и наоборот.
Часто подобные проблемы бывают вызваны разным выравниванием полей записи в памяти (у полей получаются разные смещения от начала записи).
http://www.freepascal.org/docs-html/pro ... 50001.1.60
Другой класс ошибок -- прямое чтение little-endian данных на big-endian или mid-endian платформах и наоборот.
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';... и ещё хотелось бы увидеть файл данных, сформированный программой на Delphi (кстати, какой версии), первые 3-4 записи или первые пару-тройку килобайт.
А ещё лучше, файл test.dat, сформированный вот этой программой, скомпилированной в Delphi:
Третий вариант причины получения "фигни" -- несоответствие кодировок в том, что пишет (скорее всего 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:
А ещё лучше, файл 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 раз.
- Vapaamies
- постоялец
- Сообщения: 292
- Зарегистрирован: 24.07.2012 22:37:59
- Откуда: Санкт-Петербург
- Контактная информация:
Валентина писал(а):Код: Выделить всё
type
TRec=record
Мне кажется, что записи, предназначенные для сохранения в файл, должны объявляться как packed record.
Это всего лишь один из возможных вариантов борьбы с неправильным выравниванием. Но есть и другие. И, скорее всего, в данном случае проблема не с выравниванием, а с кодировками.Vapaamies писал(а):должны объявляться как packed record
Сформированный файл с двумя строчками выглядит так:
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$Договор на по
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$Договор на по
Нет, нет, так дело не пойдёт. Нужен именно файл, а не его воспроизведение в тексте сообщения форума. В Стандартной форме редактирования сообщения есть возможность приложить файл. Либо его можно выложить на какой-нибудь файлообменник.
Если смущает наличие мусора между строками -- можете в общем случае не волноваться, он ни на что не должен влиять. Почему? Поясню. Запись:состоит из коротких строк, которые хранятся в виде (один байт, актуальная длина строки), (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) символов мусора, который находился в памяти на момент записи переменной в файл;
- и так далее.
Если имелся ввиду вот этот мусор -- заполнитель строк, то чтобы его не допускать, можно перед заполнением значений и записью в файл очищать значение переменной, что-то вроде:
Если же нужно было получить обычный текстовый файл, то что-то вроде:
А чтобы получить копию без мусора, что-то вроде:Непечатные символы в начале каждого поля -- байты длинн строк -- от этого не пропадут, и визуальное разделение на строки/записи не появится.
Если смущает наличие мусора между строками -- можете в общем случае не волноваться, он ни на что не должен влиять. Почему? Поясню. Запись:
Код: Выделить всё
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;
- поле 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.Спасибо за внимание к моей проблеме. Высылаю файлик.
"Фигня", на которую я сетовала, выглядит так в первом поле:
ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на по
вместо
113-11А
"Фигня", на которую я сетовала, выглядит так в первом поле:
ѕxФw6XсwF,P расходныйw113-11А _pE ЂOb$х c8E њх $х ЂOb
01.02.2011 Фх ЂOb$Договор на по
вместо
113-11А
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Угадать, что вы сделали с файлом, несложно: открыли в текстовом редакторе и "сохранили как" в кодировке 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 одинаковых записи:и запись по смещению 0 -- пустая. Результат получен обратным преобразованием в 1251 и отрезанием лишнего в начале.
Если хранить в utf-8, придётся пересмотреть размеры полей, например, "расходный" уже не вместится.
Во вложении пара файлов, в 1251 и utf8.
Добавлено спустя 23 минуты 13 секунд:
Пример с конвертацией при выводе (функция преобразования наколенная, посмотрите наличие более эффективных вариантов):
Прежде всего, это действие некорректно само по себе -- хотя файл и содержит вроде бы текстовые данные (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:расходный
Если хранить в 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.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Большое спасибо за помощь, все получилось! Этот этап прошла, воюю дальше...
