Файл C ++ имеет 32 КБ, разделенных символом новой строки. Мне нужно создать 8 файлов меньшего размера на каждый трюм 4096 - PullRequest
0 голосов
/ 15 марта 2020

У меня есть файл целых, разделенных разделителем новой строки.

324872
27
256230
0
45767
276143
4
258283
189
153812
214521

Размер файла - 32768 строк. Мне нужно разбить его на 8 небольших файлов по 4096 строк. Я использую fstream для потоковой передачи оригинального файла в буфер символов:

std::string fileOfInts(".txt");
char *buffer = new char[BUFFER_SIZE];
std::ifstream inputFromOrigin("origin.txt");
int fileIndex = 0;
while (inputFromOrigin)
{
    inputFromOrigin.read(buffer, BUFFER_SIZE);
    size_t count = inputFromOrigin.gcount();
    if (!count)
        break;

    std::ofstream createRunSizeFile;
    createRunSizeFile.open("fileOfInts" + std::to_string(fileIndex) + fileOfInts);
    int value;
    if (createRunSizeFile) {
        for (size_t i = 0, bufferSize = sizeof(buffer); i < bufferSize; i += sizeof(int)) {
            value = (int)buffer[i];
            createRunSizeFile << value << std::endl;
        }
    }
    createRunSizeFile.close();
    fileIndex++;
}
inputFromOrigin.close();
delete[] buffer;

Но когда я извлекаю целые числа из буфера символов, он считывает только две цифры за раз и помещает эти две цифры в один файл так, В итоге я получаю 54 файла, содержащих по одному int:

32

UPDATE:

Когда я изменяю на l oop, который присваивает значения из буфера это:

for (int i = 0; i < BUFFER_SIZE; i++)

Я получаю 4096 уникальных строк на файл, но вместо 8 файлов с одинаковыми значениями, переданными мне, я получаю 53 файла с двумя цифрами в строке:

10
49
57
57
54
48
50

Как Могу ли я проанализировать буфер символов, чтобы поместить 4096 уникальных целых в каждый файл?

ОБНОВЛЕНИЕ - Решение:

Для других, у которых может возникнуть эта проблема в будущем, вот как я адаптировал решение Дэвида к своему существующему коду:

int fileIndex = 1;
int lineIndex = 0;
std::string line = "";
std::ofstream createRunSizeFile;
// loop through origin file line by line
while (getline(inputFromOrigin, line)) {
    // when file is 0 or 4066 lines long create a new file
    if (lineIndex % RUN == 0) {
        createRunSizeFile.open("fileOfInts" + std::to_string(fileIndex) + fileOfInts);
        lineIndex = 0;
        if (createRunSizeFile.is_open()) {
            createRunSizeFile.close();
        }
        // open new run size file and increment file counter
        createRunSizeFile.open("fileOfInts" + std::to_string(fileIndex++) + fileOfInts);
        if (!createRunSizeFile.good()) {
            std::cerr << "Error: Run Size File Failed to Open" << std::endl;
            return 1;
        }
    }
    // assign line from origin to the run size file
    createRunSizeFile << line << std::endl;
    lineIndex++;
}
inputFromOrigin.close();

Я сделал ложное предположение, что будет легче извлечь int из буфера символов, чем просто построчно. Это решение делает именно то, что мне нужно сейчас.

1 Ответ

1 голос
/ 15 марта 2020

Вместо использования .read, просто используйте getline, чтобы прочитать строку, содержащую целое число, в строку. Тогда нужно просто сохранить счетчик строк и придумать схему записи суффикса для выходного имени файла, открытия выходного файла и записи строк 4096 в файл, сброса счетчика строк, открытия следующий файл и повторять до тех пор, пока не закончится чтение строк.

Вы можете либо #define константа для количества строк в подфайле, либо объявить одну, а затем объявить свои счетчики (ниже fileno просто используется как суффикс подфайла), объявите строку для использования в качестве буфера для хранения строки, прочитанной из ввода, а затем из двух ваших файлов - открыв входной файл:

#include <iostream>
#include <fstream>
#include <string>

#define NLINES 4096     /* constant no. of lines for output subfiles */

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

    if (argc < 2) { /* validate at least 1 argument for filename */
        std::cerr << "usage: " << argv[0] << " filename\n";
        return 1;
    }

    size_t  n = 0,              /* line counter */
            fileno = 1;         /* output file suffix */
    std::string s {};           /* string to use as buffer */
    std::ifstream f (argv[1]);  /* open input file stream */
    std::ofstream subf;         /* output file stream */

    if (!f.good()) { /* validate input file stream stat/e good */
        std::cerr << "error: input file open failed.\n";
        return 1;
    }

Чтобы разделить файл просто прочитайте каждую строку в s и проверьте, равен ли по модулю вашего счетчика строк ноль. Если это так, создайте ваше следующее имя выходного файла, сбросьте нулевой счетчик строк, проверьте, открыт ли ваш выходной файл, и если это так, закройте его, затем откройте выходной файл, используя новое имя выходного файла, подтвердите, что он открыт, тогда это просто записать строку в выходной файл и увеличить счетчик строк, например,

    while (getline (f, s)) {        /* read each line from input file into s */
        if (n % NLINES == 0) {      /* if 0 or 4096 */
            /* create output filename "subfile_X" */
            std::string fname = { "subfile_" + std::to_string(fileno++) };
            n = 0;                  /* reset line count 0 */
            if (subf.is_open())     /* if output file open - close it */
                subf.close();
            subf.open (fname);      /* open new output file */
            if (!subf.good()) {     /* validate output file stream state good */
                std::cerr << "error: file open failed '" << fname << "'.\n";
                return 1;
            }
        }
        subf << s << '\n';      /* write s to output file */
        n++;                    /* increment line count */
    }

Это действительно все, что вам нужно. Сшивание частей даст полную программу:

#include <iostream>
#include <fstream>
#include <string>

#define NLINES 4096     /* constant no. of lines for output subfiles */

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

    if (argc < 2) { /* validate at least 1 argument for filename */
        std::cerr << "usage: " << argv[0] << " filename\n";
        return 1;
    }

    size_t  n = 0,              /* line counter */
            fileno = 1;         /* output file suffix */
    std::string s {};           /* string to use as buffer */
    std::ifstream f (argv[1]);  /* open input file stream */
    std::ofstream subf;         /* output file stream */

    if (!f.good()) { /* validate input file stream stat/e good */
        std::cerr << "error: input file open failed.\n";
        return 1;
    }

    while (getline (f, s)) {        /* read each line from input file into s */
        if (n % NLINES == 0) {      /* if 0 or 4096 */
            /* create output filename "subfile_X" */
            std::string fname = { "subfile_" + std::to_string(fileno++) };
            n = 0;                  /* reset line count 0 */
            if (subf.is_open())     /* if output file open - close it */
                subf.close();
            subf.open (fname);      /* open new output file */
            if (!subf.good()) {     /* validate output file stream state good */
                std::cerr << "error: file open failed '" << fname << "'.\n";
                return 1;
            }
        }
        subf << s << '\n';      /* write s to output file */
        n++;                    /* increment line count */
    }
}

Пример входного файла с 32k целыми числами

$ wc -l < dat/32kint.txt
32768

Пример использования

Не очень выход:

$ ./bin/filesplit dat/32kint.txt

Результирующие подфайлы

$ for i in subfile*; do printf "%s - " "$i"; wc -l < "$i"; done
subfile_1 - 4096
subfile_2 - 4096
subfile_3 - 4096
subfile_4 - 4096
subfile_5 - 4096
subfile_6 - 4096
subfile_7 - 4096
subfile_8 - 4096

Восемь файлов по 4096 строк в каждом. Посмотрите вещи и дайте мне знать, если у вас есть какие-либо вопросы.

...