2011-01-14 28 views
1

私のウェブサイトの一部のコンテンツを更新するために、約95,000行の209MB .txtファイルが週に一度自動的にサーバーにプッシュされます。問題は、そのような大きなファイルを処理するのに十分なメモリを割り当てることができないことです。そのため、大きなファイルをそれぞれ5,000行の小さなファイルに分割したいのです。大きなファイルを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形式です。

EDIT:

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

+0

'fgets'をSecondingする。そうすれば、ファイル全体をメモリにロードせずに1行ずつ読むことができます。 – Fanis

+0

私はfgetsを使ってファイルをバイト単位で出力できます。ファイルの内容を行番号で取得するためにfgetsを使用できますか? – Jarred

答えて

3

大きなファイルがCSV形式の場合は、1行ずつ処理する必要があり、実際には小さいファイルに分割する必要はありません。一度に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でラインカウントを開始しました。

+0

ありがとう、私はこの解決策を使用しました。 – Jarred

2

あなたが行ずつを読み取るためにfgetsを使用することができます。ここでは、以下の回答を与えられたラインでファイルを読み込むためのソリューションです。

読み取った内容を新しいファイルに保存する関数を作成する必要があります。例:この後

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

、あなたは読書の各cicleに機能上startlineを渡すために再帰的にこの関数を呼び出すことができます。

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); 

これは大きなファイル等newfile1.txt、newfile2.txt、のような5000のファイルあたりの行数、および出力ファイルを経る必要があり、動作するはずですが、ループの中$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

これは、単にPHPを持つLinuxサーバ上で実行されている場合は、コマンドラインで次のコマンドを実行します:

split -l 5000 -a 4 test.txt out

は、その後、あなたが上はfopenできるファイル名に結果をグロブ。


私はあなたの孤独は厄介だと思います。なぜなら、あなたは何の理由もなくファイルを分割しているようです。 最初のデータファイルを単に開いて、それを1行ずつ読むだけでも、mysqlの挿入を実行してからファイルを削除することができます。

関連する問題