Разбить большой файл на множество небольших файлов с помощью PHP - PullRequest
1 голос
/ 14 января 2011

У меня есть 209 МБ .txt-файл с примерно 95 000 строк, который автоматически отправляется на мой сервер один раз в неделю для обновления содержимого моего веб-сайта.Проблема в том, что я не могу выделить достаточно памяти для обработки такого большого файла, поэтому я хочу разбить большой файл на более мелкие файлы по 5000 строк в каждом.

Я не могу использовать file () вообще, пока файл не будет разбитна более мелкие кусочки, поэтому я работал с SplFileObject.Но я ничего не получил с этим.Вот некоторый псевдокод того, что я хочу сделать:

read the file contents

while there are still lines left to be read in the file
    create a new file
    write the next 5000 lines to this file
    close this file

for each file created
    run mysql update queries with the new content

delete all of the files that were created

Файл в формате CSV.

РЕДАКТИРОВАТЬ: Вот решение для чтения файла построчно, с учетом ответов ниже:

function getLine($number) {
    global $handle, $index;
    $offset = $index[$number];
    fseek($handle, $offset);
    return explode("|",fgets($handle));
}

$handle = @fopen("content.txt", "r");

while (false !== ($line = fgets($handle))) {
    $index[] = ftell($handle);
}

print_r(getLine(18437));

fclose($handle);

Ответы [ 5 ]

6 голосов
/ 14 января 2011
//MySQL Connection Stuff goes here

$handle = fopen('/path/to/bigfile.txt','r');  //open big file with fopen
$f = 1; //new file number

while(!feof($handle))
{
    $newfile = fopen('/path/to/newfile' . $f . '.txt','w'); //create new file to write to with file number
    for($i = 1; $i <= 5000; $i++) //for 5000 lines
    {
        $import = fgets($handle);
        fwrite($newfile,$import);
        if(feof($handle))
        {break;} //If file ends, break loop
    }
    fclose($newfile);
    //MySQL newfile insertion stuff goes here
    $f++; //Increment newfile number
}
fclose($handle);

Это должно работать, большой файл должен проходить через 5000 строк на файл, а выходные файлы, такие как newfile1.txt, newfile2.txt и т. Д., Могут быть скорректированы с помощью бита $i <= 5000 в цикле for.

О, я вижу, вы хотите сделать вставку данных из большого файла, а не хранить информацию о файлах. Затем просто используйте fopen / fgets и вставляйте до feof.

3 голосов
/ 14 января 2011

Если ваш большой файл в формате CSV, я думаю, вам нужно обрабатывать его построчно, а на самом деле не нужно разбивать его на более мелкие файлы. Не должно быть необходимости хранить 5000 или более строк в памяти одновременно! Чтобы сделать это, просто используйте PHP «низкоуровневые» файловые функции:

$fp = fopen("path/to/file", "r");

while (false !== ($line = fgets($fp))) {
    // Process $line, e.g split it into values since it is CSV.
    $values = explode(",", $line);

    // Do stuff: Run MySQL updates, ...
}

fclose($fp);

Если вам нужен произвольный доступ, например, читать строку за строкой, вы можете создать «индекс строки» для вашего файла:

$fp = fopen("path/to/file", "r");

$index = array(0);

while (false !== ($line = fgets($fp))) {
    $index[] = ftell($fp);  // get the current byte offset
}

Теперь $index отображает номера строк в байтовые смещения, и вы можете перейти к строке, используя fseek():

function get_line($number)
{
    global $fp, $index;
    $offset = $index[$number];
    fseek($fp, $offset);
    return fgets($fp);
}

$line10 = get_line(10);

// ... Once you are done:
fclose($fp);

Обратите внимание, что я начал считать строки с 0, в отличие от текстовых редакторов.

2 голосов
/ 14 января 2011

Это должно помочь вам, у меня нет большого текстового файла, но я протестировал файл длиной 1300 строк, который разбил файл на 3 файла:

    // Store the line no:
    $i = 0;
    // Store the output file no:
    $file_count = 1;
    // Create a handle for the input file:
    $input_handle = fopen('test.txt', "r") or die("Can't open output file.");
    // Create an output file:
    $output_handle = fopen('test-'.$file_count.'.txt', "w") or die("Can't open output file.");

    // Loop through the file until you get to the end:
    while (!feof($input_handle)) 
    {
        // Read from the file:
        $buffer = fgets($input_handle);
        // Write the read data from the input file to the output file:
        fwrite($output_handle, $buffer);
        // Increment the line no:
        $i++;
        // If on the 5000th line:
        if ($i==5000)
        {
            // Reset the line no:
            $i=0;
            // Close the output file:
            fclose($output_handle);
            // Increment the output file count:
            $file_count++;
            // Create the next output file:
            $output_handle = fopen('test-'.$file_count.'.txt', "w") or die("Can't open output file.");
        }
    }
    // Close the input file:
    fclose($input_handle);
    // Close the output file:
    fclose($output_handle);

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

2 голосов
/ 14 января 2011

Вы можете использовать fgets для чтения построчно.

Вам потребуется создать функцию для помещения прочитанного содержимого в новый файл.Пример:

function load(startLine) {
    read the original file from a point startline
    puts the content into new file
}

После этого вы можете рекурсивно вызывать эту функцию для передачи startline функции в каждом цикле чтения.

1 голос
/ 14 января 2011

Если это выполняется на сервере linux, просто запустите php, чтобы командная строка выполнила следующее:

split -l 5000 -a 4 test.txt out

Затем выберите результаты для имен файлов, которые вы можете выбрать.


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

...