使用PHP将大文件分成许多较小的文件

时间:2011-01-14 18:10:48

标签: php file memory-management pseudocode

我有一个209MB .txt文件,大约95,000行,每周一次自动推送到我的服务器,以更新我网站上的一些内容。问题是我无法分配足够的内存来处理这么大的文件,因此我想将大文件分成较小的文件,每个文件有5,000行。

在文件被分成小块之前我根本不能使用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 个答案:

答案 0 :(得分:6)

//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等,可以通过for循环中的$i <= 5000位进行调整。 / p>

哦,我明白了,你想对大文件中的数据进行插入,而不是存储有关文件的信息。然后只需使用fopen / fgets并插入直到feof。

答案 1 :(得分:3)

如果你的大文件是CSV格式,我想你需要逐行处理它,而不是真的需要把它分成更小的文件。不需要一次在内存中保存5.000或更多行!要做到这一点,只需使用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 :(得分:2)

您可以使用fgets逐行阅读。

您需要创建一个函数将读取的内容放入新文件中。例如:

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

在此之后,你可以递归地调用这个函数,在每个阅读的cicle中传递startline函数。

答案 3 :(得分:2)

这应该可以帮到你,我没有一个非常大的文本文件,但是我测试了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 + mb文件时,脚本的执行时间太长。

答案 4 :(得分:1)

如果这是在linux服务器上运行,只需让php让命令行执行以下命令:

split -l 5000 -a 4 test.txt out

然后对你可以打开的文件名的结果进行全局化。


我认为你的算法很尴尬,看起来你无缘无故地分解文件。 如果您只是打开初始数据文件并逐行读取,您仍然可以执行mysql插入,然后只删除该文件。