Войти

Показать полную графическую версию : Бинарные файлы


.::.DIMA.::.
20-04-2009, 12:52
Решено

pva
20-04-2009, 15:12
Как можно прочитать бинарный файл »
Строчка, содержащая в том числе неаскишные символы, либо как последовательность однобайтовых чисел
1 символ - 1 байт »
Совершенно верно
В паскале рекомендую использовать array of char, в c++ vector<unsigned char>

pva
21-04-2009, 13:58
// с
int file_size = ...;

unsigned char* mem = (unsigned char*)malloc(file_size);
fread(mem, 1, file_size, my_file);


// c++
filebuf buf;
buf.open("my_file.txt", ios::in|ios::binary);

vector<unsigned char> mem(file_size);
mem.resize(buf.sgetn(&mem[0], file_size));

Admiral
21-04-2009, 14:08
.::.DIMA.::., fread (http://www.cplusplus.com/reference/clibrary/cstdio/fread/) нет необходимости знать структуру
К примеру по ссылке можно добавить следующий код, что б увидеть, а что хоть считало
/* the whole file is now loaded in the memory buffer. */
//...
for (int i=0;i<result;i++)
printf("%c",buffer[i]);
//...
// terminate
В случаи с fwrite всё аналогично.
В случае с fprintf / fscanf нужно задавать параметр считывания, впрочем можно ведь задать как unsigned char, а уже в цикле как по приведённом выше коде, распознать что есть что.
pva, в случаи символа, как наименьшего элемента файла, один символ = один байт.
А вот в случаи с UNICODE текста - один печатный символ уже не один байт.

pva
22-04-2009, 07:38
pva, в случаи символа, как наименьшего элемента файла, один символ = один байт.
А вот в случаи с UNICODE текста - один печатный символ уже не один байт. »
Мы ведь воспринимаем бинарные данные, я просто взял наименьшую неделимую единицу объёма памяти. А так можно было и 8-байтными double читать. А можно было и как текстовую базу данных:

struct row_t {
char id[8];
char name[100];
char description[255];
char _reserved_for_DOS_CR[2];
};

...

row_t row1[10];
fread(row1, sizeof(row_t), 10, file);

Admiral
22-04-2009, 18:47
pva насчёт юникода, тут есть пользовательский момент: если воспользоваться примером выше для чтения файла сохранённого в Юникоде, то после каждого символа будут пробелы (текст на английском, для других языков не проверял). Это удивит пользователя, который не будет наблюдать последних при просмотреть через блокнот. В этом случаи, как я понимаю, или самостоятельно убирать пробелы, или же юзать юникод версии функций чтения/записи и соответственные пользовался переменными типа wchar_t.

Хорошо что привели структуру, хотелось бы её обсудить.
Если её натравить на любой текстовый файл, то вывод будет в виде абракадабры ╠╠Ё:B Это вполне логично, ведь как известно при чтении/записи структуры fread/fwrite оперирует с так называемыми "сырыми" данными.id0 ММММname0 У блокнота на это счёт был другой вариант с многократным использованием символа [] и цифр.
И дело тут не в режиме открытия t/b, которые отличаются только тем, что в первом случаи перевод каретки (Ентер) и новая строчка это один символ, а во втором два отдельных, а в том что так оно устроено.
Собственно сама программа чтения/записи, с использованиям приведённой структуры.
Поля, да и сама структура, используются явно не по назначению, приведено только для примера.
#include <stdio.h>
#include <stdlib.h>

struct row_t
{
char id[8];
char name[100];
char description[255];
char _reserved_for_DOS_CR[2];
};

int main(int argc, char* argv[])
{

row_t row1[10];
int i;
for (i=0;i<10;i++)
{
sprintf(row1[i].id,"id%d",i);
sprintf(row1[i].name,"name%d",i);
sprintf(row1[i].description,"description%d",i);
sprintf(row1[i]._reserved_for_DOS_CR,"R%d",i);
}

if (argc>1)
{
FILE * pFile = fopen ( argv[1] , "wb+" );
if (pFile==NULL)
{
printf ("File error");
return -1;
}


fwrite(row1, sizeof(row_t), 10, pFile);
rewind(pFile);

for (i=0;i<10;i++)
{
sprintf(row1[i].id,"%d",i);
sprintf(row1[i].name,"%d",i);
sprintf(row1[i].description,"%d",i);
sprintf(row1[i]._reserved_for_DOS_CR,"%d",i);
}

fread(row1, sizeof(row_t), 10, pFile);
for (i=0;i<10;i++)
printf("%s %s %s %s\n", row1[i].id, row1[i].name, row1[i].description, row1[i]._reserved_for_DOS_CR);
fclose (pFile);
}
return 0;
}
Использование: набрать в командной строке prog.exe somefile.dat, где имя программы соответственное, имя файла любое. Будут создан автоматически.
id0 name0 description0 R0id1
id1 name1 description1 R1id2
id2 name2 description2 R2id3
id3 name3 description3 R3id4
id4 name4 description4 R4id5
id5 name5 description5 R5id6
id6 name6 description6 R6id7
id7 name7 description7 R7id8
id8 name8 description8 R8id9
id9 name9 description9 R9

Мне не понятно, почему идёт захват следующего элемента, я про R0id1 когда должен был выведен только R0
При отладки в некоторых средах может появляется сообщение о разрушении стека возле переменной row1.
Run-Time Check Failure #2 - Stack around the variable 'row1' was corrupted.
Избавится от него можно уменьшив в начале программы количество заполняемых элементов массива или убрав заполнение элемента структуры _reserved_for_DOS_CR.
Причём то что далее по тексту идёт затирание чрез i (для того что б убедится что данные берутся именно из файла) всех элементов структуры и элементов массива (от 0 до 10 не включая 10) row1 среда не находит проблемным.

Структуры удобны в обработке, но в текстовом виде данные в файлы не сохранишь. Или всё же можно?

pva
22-04-2009, 21:18
выше для чтения файла сохранённого в Юникоде »
Изначально речь шла о двоичном представлении (и возможно уникода, но не обязательно), хотя может я пропустил какие-то детали
или самостоятельно убирать пробелы »
Это не пробелы, а байт 0, т.к. для символов ascii значение не превышает 127, а в 16-битном представлении это даёт старший нулевой байтоперирует с так называемыми "сырыми" данными. »
не согласен, они так не называются, эти функции оперируют с содержимым памяти (независимо от представления её в программе)
Или всё же можно? »
можно всё на свете. Современные компиляторы уже не ограничивают фантазию программиста.
Пример: запускаем FAR, создаём в нём текст, который содержит ровно 20 строк ровно по 8 + 100 + 255 = 363 символа (пробела). Включаем режим замены (нажимаем INSERT) и заполняем полученные строки (так чтобы длина строки оставалась 363, а "пустые" символы заполнялись пробелами) Начиная с каждого 0 символа строки пишем ID, с каждого 8 name, со 108 - description. На вид получится красивая выровненная табличка. Сохраняем файл и подаём в качестве аргумента к программе. Должно отработать красиво.
Конечно же не оптимально хранить данные в таких файлах, где много пробелов, тем не менее, бывают случаи когда это самое элегантное решение, не требующее сложной обработки данных. Например рассмотрим телефонную станцию, после каждого звонка выдаёт следующие строчки:
1. дата (8 симв, ддммгггг)
2. время (6 символов ччммсс)
3. исходящий номер (11 симв)
4. входящий номер (11 симв)
5. время разговора (4 симв)
6. стоимость разговора (10 симв)
вот в таком виде:

220420092259148916342111189076958093 120 4500
220420092304548916342111189076958093 120 4500

Теперь по поводу ошибок в программе:

// думаю причина в использовании аргумента %s с не Null-terminated string
// правильный вариант:
void print_row(row_t* row)
{
printf("%8s|%100s|%255s|\n", row->id, row->name, row->description);
// static const char* space = "|";
// fwrite(row->id, sizeof(row->id), 1, stdout);
// fwrite(space, 1, 1, stdout);
// fwrite(row->name, sizeof(row->name), 1, stdout);
// fwrite(space, 1, 1, stdout);
// fwrite(row->description, sizeof(row->description), 1, stdout);
// fwrite(space, 1, 1, stdout);
}

Admiral
23-04-2009, 01:48
pva, это про то, как с точки зрения пользователя будет работать программа, читающие так текстовые файлы.
Опять использовал пользовательскую терминологию, на экране он видит пробелы.
Спасибо за ликбез, буду знать - "байт 0".

Говоря "сырые" я использую терминологию применяемую Харви и Полом Дейтелем, может быть дело в переводе. Тогда мне не понятно, почему находясь в памяти компьютера эти данные имеют читабельный вид, а сохраняясь в файл преображаются в не читаемый вид?

А почему именно 20? У нас ведь массив на 109.

Чёткость не меньше/не больше, в данном случаи в файле 20 строк, не совсем согласуется с тем, что данные в файле могут свободно обновляться, в том числе сторонней программой, например через блокнот пользователём.

К сожалению отображение не получилось ID name description
01 and description
02 asm description
03 auto description
04 bitand description
05 bitor description
06 bool description
07 break description
08 case description
09 catch description
10 char description
11 class description
12 compl description
13 const description
14 const_cast description
15 continue description
16 default description
17 delete description
18 do description
19 double description
#include <stdio.h>

struct row_t
{
char id[8];
char name[100];
char description[255];
char _reserved_for_DOS_CR[2];
};

int main(int argc, char* argv[])
{
if (argc>1)
{
FILE * pFile = fopen ( argv[1] , "rb" );
if (pFile==NULL)
{
printf ("File error");
return -1;
}

row_t row1[10];
fread(row1, sizeof(row_t), 10, pFile);
for (int i=0;i<10;i++)
printf("%s %s %s", row1[i].id, row1[i].name, row1[i].description);
fclose (pFile);
}
return 0;
}

А можно ли вместо пробелов применять разделять данные элементов структуры табуляцией? Простой пример - таблицы в Екселе: сохраняем в табулированный файл и обрабатываем в программе.

pva
23-04-2009, 08:55
Тогда мне не понятно, почему находясь в памяти компьютера эти данные имеют читабельный вид, а сохраняясь в файл преображаются в не читаемый вид? »
Дело в том, что во время чтения "в читабельном виде", эти данные реально находятся в памяти (программа-просмотрщик перед отрисовкой на экран читает их из файла), а "читабельность" зависит от представления данных в программе. Например:

union
{
double d;
char c[8];
} value1;

value1.d = 0.1;
// используем одну и ту же память:
printf("%8s", value1.c); // получим галиматью
printf("%8lf", value1.d); // получим красоту
printf("%8ld", value1.d); // в "с" это можно сделать :( получим галиматью

// получим 16-ричное представление памяти, в которой лежит double(0.1)
cout << hex << setfill('0') ;
for(unsigned i=0; i<8; ++i) cout << setw(2) << static_cast<unsigned>(reinterpret_cast<unsigned char>(c[i])) << " ";

А почему именно 20? У нас ведь массив на 109. »
задавая структуру row_t я ограничил размер неделимого блока файла (сохранающий работоспособность программы) до 365 байт. Других ограничений нет. Можно загрузить за раз 20 строчек, можно 50, а можно 1. Главное соблюдать в программе нерушимость памяти.
Чёткость не меньше/не больше, в данном случаи в файле 20 строк, не совсем согласуется с тем, что данные в файле могут свободно обновляться, в том числе сторонней программой, например через блокнот пользователём »
Если пользователю давать работать с файлом только через программу, то всё будет ок! А уронить ручкми можно любой файл.
К сожалению отображение не получилось в виде чёткой таблицы (применяемы файл данных и код программы, для чистоты эксперимента, без исправлений вывода, во вложении). »
Я поправил файл (нам строчки были разной длины). В скриншоте всё выглядит достаточно ровно

Admiral
25-04-2009, 20:29
pva, возможно покажусь назойливым, но всё же, возможно ли структуру в читабельном виде сохранить в файл, что б при чтении обратно программа понимала что это структура? В примерах про структуры показывается что нет - только бинарное представление.
В каких случаях стоит использовать union(объединение)? Это ведь таже структура, только более щадящая память за счёт размещения всех полей по одному и тому же адресу.
[hr]
Вместо пробелов всё же хочется задействовать табуляцию. В качестве примера я взял предыдущий - про телефонную станцию.
Вот её структура
struct PhoneStatistic
{
char date[8+1]; //1. дата (8 симв, ддммгггг)
char time[6+1]; //2. время (6 символов ччммсс)
char dialing[11+1]; //3. исходящий номер (11 симв)
char incoming[11+1]; //4. входящий номер (11 симв)
char duration[4+1]; //5. время разговора (4 симв)
char price[10+1]; //6. стоимость разговора (10 симв)
char *notes; //7. новое поле переменно не определённой/не известной длины для эксперимента
};
ещё один прибавляем для NULL символа
Визуально всё хорошо получается (используя printf), да вот возникает сложность сбора данных в поля структуры.

Используя printf можно в цикле выводить символы, при этом на экране складывается слово. По идеи, тоже самое можно сделать для полей структуры через sprintf. Да вот не задачка, не проходит.

stdout это стандартный поток вывод, а stdin - ввода. Возможно, ли каким-то образом используя их комбинацию заполнять поля структуры данными выводимыми printf'ом? То есть что б они брались с экрана?

Возможно ли во время выполнения программы узнать количество полей/элементов структуры? Так как в программе приходится за каждым элементом закреплять свой цикл.

/* fread example: read a complete file */

#include <stdio.h>
#include <stdlib.h>
//#include <string.h>

#define CR 0x0d //Возврат каретки
#define SP 0x20 //Пробел

struct PhoneStatistic
{
char date[8+1]; //1. дата (8 симв, ддммгггг)
char time[6+1]; //2. время (6 символов ччммсс)
char dialing[11+1]; //3. исходящий номер (11 симв)
char incoming[11+1];//4. входящий номер (11 симв)
char duration[4+1]; //5. время разговора (4 симв)
char price[10+1]; //6. стоимость разговора (10 симв)
char *notes; //7. новое поле переменно не определённой/не известной длины для эксперимента
};

int main(int argc, char* argv[])
{
if (argc<=1)
{
printf("\nUsage: %s <File Name>",argv[0]);
return 0;
}

FILE * pFile;
long lSize;
char * buffer;
size_t result;

pFile = fopen ( argv[1] , "rb" );
if (pFile==NULL) {fputs ("File error",stderr); exit (1);}

// obtain file size:
fseek (pFile , 0 , SEEK_END);
lSize = ftell (pFile);
rewind (pFile);

// allocate memory to contain the whole file:
buffer = (char*) malloc (sizeof(char)*lSize);
if (buffer == NULL) {fputs ("Memory error",stderr); exit (2);}

// copy the file into the buffer:
result = fread (buffer,1,lSize,pFile);
if (result != lSize) {fputs ("Reading error",stderr); exit (3);}

/* the whole file is now loaded in the memory buffer. */
fclose (pFile);

unsigned int i;
unsigned int Tabs=0;
unsigned int Enters=0;
unsigned int Newlines=0;
unsigned int Spaces=0;

for (i=0;i<result;i++)
{
//printf("%c",buffer[i]);
/*Если раскомментировать строчку выше,
то всё что после данного цикла до free (buffer);
можно убрать и визуально вывод не пострадает.*/
switch (buffer[i])
{
case '\t': Tabs++; break;
case '\n': Enters++; break;
case CR: Newlines++; break;
case SP: Spaces++; break;
default: break;
}
}
printf("\t\tStatistics\n Pages *\n Words *\n Characters \t\t\t%i\n Characters (with spaces) \t%i\n Tabs \t\t\t\t%i\n Entered \t\t\t%i\n Lines \t\t\t\t%i\n Spaces \t\t\t%i\n",i-Spaces,i-Enters,Tabs,Enters,Newlines,Spaces);

PhoneStatistic *currentStatistic = new PhoneStatistic[Newlines];
/*Возможно покажется что данные булевые переменные,
но они рассчитаны, по крайне мере запланированы,
на тот случай если структура файла будет нарушена*/
bool lockDate = false;
bool lockTime = true;
bool lockDialing = true;
bool lockIncoming = true;
bool lockDuration = true;
bool lockPrice = true;
bool lockNotes = true;
//unsigned int j;

for(i=0;i<result;i++)
{
while ((buffer[i]!='\t') && (!lockDate))
{
if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
i++;
}
lockDate = true;
printf("%c",buffer[i]);//здесь и дальше вне циклов while будет вывод для визуальной разметки, табуляции и символы новой строки
i++;
lockTime = false;

while ((buffer[i]!='\t') && (!lockTime))
{
if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].time,"%c",buffer[i]);
//printf("%c",currentStatistic[j].time);
i++;
}

lockTime = true;
printf("%c",buffer[i]);
i++;
lockDialing = false;

while ((buffer[i]!='\t') && (!lockDialing))
{
if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].dialing,"%c",buffer[i]);
//printf("%c",currentStatistic[j].dialing);
i++;
}

lockDialing = true;
printf("%c",buffer[i]);
i++;
lockIncoming = false;

while ((buffer[i]!='\t') && (!lockIncoming))
{ if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].incoming,"%c",buffer[i]);
//printf("%c",currentStatistic[j].incoming);
i++;
}

lockIncoming = true;
printf("%c",buffer[i]);
i++;
lockDuration = false;

while ((buffer[i]!='\t') && (!lockDuration))
{ if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].duration,"%c",buffer[i]);
//printf("%c",currentStatistic[j].duration);
i++;
}

lockDuration = true;
printf("%c",buffer[i]);
i++;
lockPrice = false;

while ((buffer[i]!='\t') && (!lockPrice))
{ if (buffer[i]=='\n') break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].price,"%c",buffer[i]);
//printf("%c",currentStatistic[j].price);
i++;
}

lockPrice = true;
printf("%c",buffer[i]);
i++;
lockNotes = false;

while ((buffer[i]!='\n') && (!lockNotes))
{
if (i==result) break;
printf("%c",buffer[i]);
//sprintf(currentStatistic[j].notes,"%c",buffer[i]);
//printf("%c",currentStatistic[j].notes);
i++;
}

//if (buffer[i]=='\n') j++;
if (i==result) break;
lockNotes = true;
printf("%c",buffer[i]);
//i++; Дадим циклу for увеличить счётчик :)
lockDate = false;
}
free (buffer);
//currentStatistic if ((buffer[i]!=CR)/*0x0d*/
//printf("\n");

//for (int j=0;j<Newlines;j++)
// printf("%s",currentStatistic[j].date);

return 0;
}
22042009 225914 89163421111 89076958093 120 4500 importante
22042009 230454 89163421111 89076958093 120 4500 secure
Отличие, от данных которые привёл pva выше - в добавленной табуляции между полями + новое поле.

pva
27-04-2009, 08:41
возможно покажусь назойливым, но всё же, возможно ли структуру в читабельном виде сохранить в файл, что б при чтении обратно программа понимала что это структура? »
Если выгружать память (без обработки), а структура содержит непечатные символы, то нельзя. Если загружать-выгружать память, нужно понимать что прявя файл правишь (а соответственно нарушаешь) память программы. Если программа в памяти старается держать читабельный текст (например использовать не Null-terminated strings а space-padded strings)
Кстати вместо назойливости я углядел желание докопаться до истины
Используя printf можно в цикле выводить символы, при этом на экране складывается слово. По идеи, тоже самое можно сделать для полей структуры через sprintf. Да вот не задачка, не проходит. »
Не понял мысль
То есть что б они брались с экрана? »
потоки ввода-вывода наоборот сделаны так, чтобы можно было работать без экрана, например с файлом. И тут надо договориться как вы воспринимаете входящй поток. Либо как текст (в удобном для человека виде), либо как данные (данные уже поступают в таком виде, в котором коду "удобно" с ними работать). Насколько я понял речь скорее идёт о использовании конвейеров: c:> someprogram1.exe | someprogram2.exe
Возможно ли во время выполнения программы узнать количество полей/элементов структуры? Так как в программе приходится за каждым элементом закреплять свой цикл »
Если не держать информацию о структуре данных в программе, то нельзя. Язык c/c++ не поддерживает эту возможность. Можно поступить так:

struct _runtime_info_entry {unsigned offset; unsigned size;};

struct data1
{
char a[10], b[20], c[35];
static const _runtime_info_entry* info;
}

_runtime_info_entry* data1::info = {{0,10},{10,20},{30,35},{0,0}};

// вариант для C
void load(void* data, const _runtime_info_entry* info)
{
for(; info->size!=0; ++info)
{
fread(reinterpret_cast<char*>(data) + info->offset, info->size, stdin);
}
}

// вариант для C++, вообще сомнительный конечно, потому что не безопасный
template<typename Struct1>
void load(Struct1& data, filebuf& buf)
{
char* data_ptr = reinterpret_cast<char*>(&data);
const _runtime_info_entry* info = Struct1::info;

for(; info->size!=0; ++info)
{
fill(data_ptr + buf.sgetn(data_ptr + info->offset, info->size), data_ptr + info->size, ' ');
}
}

а ещё можно использовать виртуальную функцию virtual data1::info(), которая возращает информацию об объекте (так делается в QT), но мне всё-таки кажется лучше определить функции load() и save().

Admiral
27-04-2009, 20:23
pva спасибо за комплимент.
Не понял мысль »
#include <stdio.h>

int main(int argc, char* argv[])
{
int i=0;
char HW [11+1]="Hello World";

while (HW[i]!='\0')
{
printf("%c",HW[i]);
i++;
}
printf("%s",HW);
return 0;
}
В программе дважды выведена строчка Hello World, первый раз как символьный массив, второй раз как строчка с помощью указателя.
В программе посимвольно проверяются данные, с помощью sprintf не выходит их складывать в переменную-элемент структуры. Да и к тому же нужно нуль символ учитывать.

Да, так оно и будет Excel-> TSV.txt -> Program.exe

pva
28-04-2009, 09:46
В программе посимвольно проверяются данные, с помощью sprintf не выходит их складывать в переменную-элемент структуры »
Потому что надо договариваться об интернтерфейсе между программами. Если вывод одной программы - форматированный текст, значит программа, которая этот вывод читает, должна пользоваться функциями форматированного текста. Если вывод - поток двоичных данных, программа читающая должна воспринимать их как двоичные данные. Пример:
функции printf, scanf - для форматированного ввода-вывода
функции fread, fwrite - для двоичного.
Некорректно ожидать, что вывод в цикле символов без разделителей пробелами функцией printf будет на ура воспринят функцие scanf (которая, к примеру, пробелами проверяет ограницы полей ввода).
Ещё пример несовпадения интерфейсов:

Массив чисел в нотации pascal: (1, 2, 3, 4, 5)
Массив чисел в нотации c/c++: {1, 2, 3, 4, 5}
Строчка данных в файле csv: 1; 2; 3; 4; 5
Строчка данных, воспринимаемая кодом (числа, разделённые пробелами короче)
copy(istream_iterator<int>(cin), istream_iterator<int>(), back_inserter<int>(vector_of_int))
1 2 3 4 5

Как видите, одно и то же можно выразить разными способами. А чтобы передать данные, нужно чтобы операции чтения и записи пользовались одним языком (нотацией).

pva
04-05-2009, 06:44
и не зная её. »это уж как повезёт. Можно угадать, а можно и нет. Вот например ни за что не догадаешься, как файл mp3 на экран выводить

pva
05-05-2009, 07:19
они проверяют по сигнатурам, то есть есть шаблонные файлы типа регулярных выражений (regular expressions), которые выявляют наиболее характерные для вирусов в базе антивируса куски кода в экзешнике. А эвристический анализ разбирает исполняемый код и вычисляет вероятность того, что код может нанести компьютеру вред.




© OSzone.net 2001-2012