Вопрос по c, linux – чтение и запись кусками на Linux с помощью C

7

У меня есть ASCII-файл, где каждая строка содержит запись переменной длины. Например

Record-1:15 characters
Record-2:200 characters
Record-3:500 characters
...
...
Record-n: X characters

Поскольку размер файла составляет около 10 ГБ, я хотел бы прочитать запись кусками. После прочтения мне нужно преобразовать их, записать в другой файл в двоичном формате.

Итак, для чтения моей первой реакцией было создание массива char, такого как

FILE *stream; 
char buffer[104857600]; //100 MB char array
fread(buffer, sizeof(buffer), 104857600, stream);
  1. Is it correct to assume, that linux will issue one system call and fetch the entire 100MB?
  2. As the records are separated by new line, i search for character by character for a new line character in the buffer and reconstruct each record.

Мой вопрос заключается в том, как я должен читать в блоках или есть лучшая альтернатива для чтения данных в блоках и воссоздать каждую запись? Есть ли альтернативный способ чтения x числа строк переменного размера из файла ASCII за один вызов?

Далее во время записи я делаю то же самое. У меня есть буфер записи символов, который я передаю fwrite, чтобы записать целый набор записей за один вызов.

fwrite(buffer, sizeof(buffer), 104857600, stream);

ОБНОВЛЕНИЕ: Если бы я установил setbuf (поток, буфер), где буфер - мой 100-мегабайтный буфер символов, fgets возвратился бы из буфера или вызвал дисковый ввод-вывод?

Посмотрите в Fgets, он будет получать по одной строке за вас, если хотите. Richard J. Ross III
Я хотел бы избежать построчного чтения, но предпочел бы чтение строк переменного размера за один раз. Кроме того, с fgets () мне нужно иметь буфер, в который помещается самая длинная строка. Поскольку мой размер записи может варьироваться от нескольких сотен байтов до 16 МБ, я бы тратил впустую память. Jimm

Ваш Ответ

3   ответа
2

mmapФайл будет проще всего.mmap отображает (часть) файла в память, так что весь файл может быть доступен по существу как массив байтов. В вашем случае вы не сможете отобразить весь файл сразу, он будет выглядеть примерно так:

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>


/* ... */

struct stat stat_buf;
long pagesz = sysconf(_SC_PAGESIZE);
int fd = fileno(stream);
off_t line_start = 0;
char *file_chunk = NULL;
char *input_line;
off_t cur_off = 0;
off_t map_offset = 0;
/* map 16M plus pagesize to ensure any record <= 16M will always fit in the mapped area */
size_t map_size = 16*1024*1024+pagesz;
if (map_offset + map_size > stat_buf.st_size) {
  map_size = stat_buf.st_size - map_offset;
}
fstat(fd, &stat_buf);
/* map the first chunk of the file */
file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset);
// until we reach the end of the file
while (cur_off < stat_buf.st_size) {
  /* check if we're about to read outside the current chunk */
  if (!(cur_off-map_offset < map_size)) {
    // destroy the previous mapping
    munmap(file_chunk, map_size);
    // round down to the page before line_start
    map_offset = (line_start/pagesz)*pagesz;
    // limit mapped region to size of file
    if (map_offset + map_size > stat_buf.st_size) {
      map_size = stat_buf.st_size - map_offset;
    }
    // map the next chunk
    file_chunk = mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd, map_offset);
    // adjust the line start for the new mapping
    input_line = &file_chunk[line_start-map_offset];
  }
  if (file_chunk[cur_off-map_offset] == '\n') {
    // found a new line, process the current line
    process_line(input_line, cur_off-line_start);
    // set up for the next one
    line_start = cur_off+1;
    input_line = &file_chunk[line_start-map_offset];
  }
  cur_off++;
}

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

char *file_data = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
0

fgets(buff) для автоматического определения новой строки.

а затем использоватьstrlen(buff) для подсчета размера буфера,

if( (total+strlen(buff)) > 104857600 )

тогда пиши в новый кусок ..

Но размер фрагмента вряд ли будет 104857600 байт.

CMIIW

Error: User Rate Limit Exceeded Jimm
fgetsError: User Rate Limit Exceededchar*, int, а такжеFILE*Error: User Rate Limit Exceededchar*
6

fread will fetch the entire thing at once. (Assuming it's a regular file.) But it won't read 105 MB unless the file itself is 105 MB, and if you don't check the return value you have no way of knowing how much data was actually read, or if there was an error.

Use fgets (see man fgets) instead of fread. This will search for the line breaks for you.

char linebuf[1000];
FILE *file = ...;
while (fgets(linebuf, sizeof(linebuf), file) {
    // decode one line
}

There is a problem with your code.

char buffer[104857600]; // too big

If you try to allocate a large buffer (105 MB is certainly large) on the stack, then it will fail and your program will crash. If you need a buffer that big, you will have to allocate it on the heap with malloc or similar. I'd certainly keep stack usage for a single function in the tens of KB at most, although you could probably get away with a few MB on most stock Linux systems.

В качестве альтернативы, вы могли бы простоmmap весь файл в память. Это не улучшит и не ухудшит производительность в большинстве случаев, но с ним легче работать.

int r, fdes;
struct stat st;
void *ptr;
size_t sz;

fdes = open(filename, O_RDONLY);
if (fdes < 0) abort();
r = fstat(fdes, &st);
if (r) abort();
if (st.st_size > (size_t) -1) abort(); // too big to map
sz = st.st_size;
ptr = mmap(NULL, sz, PROT_READ, MAP_SHARED, fdes, 0);
if (ptr == MAP_FAILED) abort();
close(fdes); // file no longer needed

// now, ptr has the data, sz has the data length
// you can use ordinary string functions

Преимущество использованияmmap в том, что вашей программе не хватило памяти. В 64-разрядной системе вы можете одновременно поместить весь файл в свое адресное пространство (даже файл размером 10 ГБ), и система будет автоматически читать новые фрагменты, когда ваша программа обращается к памяти. Старые куски будут автоматически отброшены и перечитаны, если вашей программе они снова понадобятся.

Это очень хороший способ просматривать большие файлы.

Похожие вопросы