300~500pt 完全回答をお願いします。補足なども読んでください。質問はコメント欄にお願いします。
アドレス帳のような、どでかいXMLファイルがあります。後で処理するには重すぎるので、ファイル分割ツールを使って適当な大きさに分割しましたが、xml構造を無視して分割してしまうので、整合性をとれる形にしたいと思っています。
次のようなxmlファイルです
addressbooktokyo.xmlの中身
<xml version="1.0" encoding="utf-8"?>
<myaddress version="1.0">
<header>
<各種ヘッダー情報、たとえば地域などファイルによって微妙に違う>
</header>
<body>
<person>
<name>山田さん</name>
<phone>0123456789</phone>
</person>
膨大な繰り返し・・・
</body>
</myaddress>
質問などはコメントにお願いします。
分割されたファイルは次のようになります。
addressbooktokyo000.xml
addressbooktokyo001.xml
addressbooktokyo002.xml
addressbooktokyo003.xml
・・・
addressbooktokyo050.xml
初回のファイルのみにヘッダ(開始から</header>まで)があります。末尾のファイルのみにフッタ(</body></myaddress>)があります。
作業の流れ
1. 000ファイルを開いて、ヘッダをどこかに保存する
2. 001ファイルを開いて、開始から最初の</person>までを削除しつつ、変数に読み込んで、000の末尾に追加する
3. 000の末尾にフッタ(</body></myaddress>)を追加して、保存して閉じる
4. 001の開始部分にヘッダを挿入する
5. 002ファイルを開いて、開始から最初の</person>までを削除しつつ、変数に読み込んで、001の末尾に追加する
3. 001の末尾にフッタ(</body></myaddress>)を追加して、保存して閉じる
以下ループ
最後. 最後のファイルのみ、フッタはつけずに保存して閉じる
以上です。
以前に、次のURLで質問をしたのですが、対象のXMLファイルがあまりにも大きすぎたので、ファイル分割ツールで指定したサイズにぶった切ってから、あとで整合性をとるやり方に切り替えたいと思います(分割ツールは速いんです)。
いろいろ考えてみましたが、なかなか納得できる結果ではなかったので参考出品です。
100M のファイルを 10M に分割するのは数十秒でしたが 数Gのファイルを処理する時間は未確認ですので、前回のスクリプトと大差ないようでしたら(&ほかに回答なければ)、質問をキャンセルください。
PHP は使用可能ということなので、PHP での実装です。
なお、分割したファイルを処理するのではなく、直接分割する例です。
下記を適当な名前(divFile.php)等で処理するファイルがあるフォルダに保存し、
コマンドラインで php.exe のフルパスに引数でスクリプトを指定し実行してみてください。
下記はCドライブの Program Files 下に PHPがインストールされている実行例です。
C:\>"C:\Program Files\PHP\php.exe" divFile.php
<?php // 引数は処理するファイル名 DivideFile( 'sample.xml' ); //--------------------------------------------------------------------- function DivideFile( $filePath ) //--------------------------------------------------------------------- { // 処理用データ //------------------------ $headerKeyword = "<body>"; // ヘッダ部分の最終キーワード $footerKeyword = "</body>"; // フッダ部分の先頭キーワード $recordKeyword = "</person>"; // 分割内の最終キーワード $fileSize = 10 * 1024 * 1024; // 分割サイズ ex) 10MB // 読込ファイルのオープン //------------------------ $rfp = fopen( $filePath, "rb" ); // ヘッダの切り出し //------------------------ $readData = fread( $rfp, $fileSize ); $spos = strpos( $readData, $headerKeyword ); if( $spos === false ) { echo "分割サイズ内に".$headerKeyword."がありません。"; return; } while( ord($readData[$spos]) != 13 ) { $spos++; } if( ord($readData[$spos+1]) == 10 ) { $spos++; } $header = substr($readData, 0, $spos + 1); // フッダの切り出し //------------------------ fseek( $rfp, $fileSize * (-1), SEEK_END ); $readData = fread( $rfp, $fileSize ); $epos = strrpos( $readData, $footerKeyword ); if( $epos === false ) { echo "分割サイズ内に".$footerKeyword."がありません。"; return; } while( ord($readData[$epos]) != 13 && ord($readData[$epos]) != 10 ) { $epos--; } $footer = substr($readData, $epos + 1 ); // サイズの整合 //------------------------ $fileSize -= strlen( $header ); $fileSize -= strlen( $footer ); // 分割処理 //------------------------ fseek( $rfp, $spos + 2, SEEK_SET ); $restBuffer = ""; $fileIndex = 1; while( !feof( $rfp ) ){ $readBuffer = fread( $rfp, $fileSize ); if ( $readBuffer === false ) { echo "ファイルの読み込みに失敗しました。\n"; return; } $readBuffer = $restBuffer.$readBuffer; if( ( $pos = strrpos( $readBuffer, $recordKeyword ) ) === false ) { echo "検索文字が見つかりませんでした。\n"; return; } if ( strpos( $readBuffer, "\n", $pos ) === false ){ $pos = strrpos( $readBuffer, $recordKeyword, $pos - strlen($readBuffer) - 1 ); } while( ord($readBuffer[$pos]) != 13 && $pos < strlen($readBuffer) ) { $pos++; } if( ord($readBuffer[$pos+1]) == 10 ) { $pos++; } // 出力ファイル名 echo "--->". $fileIndex." 番目のファイルを書き出しています\n"; $wFile = sprintf( 'sample_%03d.xml',$fileIndex ); $wfp = fopen( $wFile, "wb" ); fwrite( $wfp, $header ); fwrite( $wfp, substr($readBuffer, 0, $pos + 1) ); fwrite( $wfp, $footer ); fclose( $wfp ); $restBuffer = substr( $readBuffer, $pos ); $fileIndex++; } } ?>
ありがとうございます!
2012/12/20 13:05:49さっそく試してみたのですが・・・やはり処理がだいぶ重くなってしまいました。
結局、行単位で分割できるツールを導入して、1ファイルを1万行(6.5MBくらい)に分割して、自作したVBSで前後の整合性をとることにしました。6MB強のファイルが450くらいありましたが、1分ちょっとで整形が終わりました。
VBSにしろPHPにしろ、やはり一度に処理するファイルの大きさを小さくするといろいろスムーズですね。当然と言えば当然ですが。
450ファイル×6.5MBを後処理のPHPに回してみたところ、1ファイル20秒前後で処理されて、しばらく放置していたら300万エントリがDBに追加されていました。
やはり大容量ファイルを扱うにはコマンドベースの処理が必要のようですね。
2012/12/20 13:55:20他に回答なければ、質問はキャンセル下さい。