2011-01-14 51 views
1

我有一個209MB的.txt文件,其中大約95,000行會自動推送到我的服務器,每週更新一次我的網站上的一些內容。問題是我無法分配足夠的內存來處理這樣一個大文件,所以我想將大文件分解成每個有5000行的較小文件。使用PHP將大文件分解爲許多較小的文件

我不能使用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); 
+2

你正在嘗試什麼樣的處理?通過`fopen`和`fgets`進行讀取應該可以正常工作,除非您試圖將其全部存儲在數組中。 – mfonda 2011-01-14 18:18:17

+0

借調`fgets`。這樣你可以一行一行地讀取,而不需要將整個文件加載到內存中。 – Fanis 2011-01-14 18:19:24

回答

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

可以使用fgets按行讀入行。

您需要創建一個函數來將讀取的內容放到一個新文件中。例如:

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

在此之後,你可以遞歸調用此函數的函數傳遞startline在閱讀各cicle。

5
//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位的循環進行調整。

哦,我明白了,你想插入大文件中的數據,而不是存儲有關文件的信息。然後使用fopen/fgets並插入,直到feof。

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文件時,腳本的執行時間太長。

2

如果這是一個Linux服務器上運行只需使PHP在命令行中執行以下命令:

split -l 5000 -a 4 test.txt out

glob的。然後的結果,你可以上的fopen文件名。


我認爲你的算法很尷尬,看起來像你無故分手文件。 如果你只是打開初始數據文件並逐行閱讀,你仍然可以執行mysql插入,然後刪除文件。

相關問題