PHPの文字列の各行を反復する


130

ユーザーがテキストファイルをアップロードするか、ファイルのコンテンツをテキストエリアにコピーして貼り付けることができるフォームがあります。2つを簡単に区別して、入力したものを文字列変数に入れることができますが、そこからどこに行くのですか?

文字列の各行を反復処理する必要があります(できれば、異なるマシンの改行を気にしないでください)、トークンが1つだけあることを確認し(スペース、タブ、コンマなどがない)、データを無害化してから、SQLクエリを生成しますすべての行に基づいています。

私はかなり優れたプログラマーなので、その方法についての一般的な考え方は知っていますが、PHPを使用してから長い間、間違ったものを探していて、役に立たない情報を思いついているように感じます。私が抱えている重要な問題は、文字列の内容を行ごとに読みたいということです。ファイルの場合は簡単です。

私は主に、それを行うためのアルゴリズムではなく、有用なPHP関数を探しています。助言がありますか?


最初に改行を正規化することができます。このメソッドs($myString)->normalizeLineEndings()は、github.com / delight-im / PHP-Str(MITライセンスに基づくライブラリ)で利用できます。ソースコードを確認することをお勧めします。
16

回答:


190

preg_split テキストを含む変数。返された配列を反復処理します。

foreach(preg_split("/((\r?\n)|(\r\n?))/", $subject) as $line){
    // do stuff with $line
} 

これは^ Mを\ n \ rに加えて処理しますか?
Topher Fangio

変数内に配置されたアスキーキャリッジリターンが\ rに変換されるかどうかはわかりません。そうでない場合は、代わりに常にascii値を指定してsplit()/ exlope()を使用できます-ch(13)
Kyril

12
より良い正規表現は/((\r?\n)|(\r\n?))/です。
フェリックスSaparelli

3
UnixのLF(\ n)は、MacOSの<9 CR(\ r)は、WindowsのCR + LF(\ rを\ n)とまれLF + CR(\ nは\ r)を一致させるには、それはする必要があります:/((\r?\n)|(\n?\r))/
Devのを待っています...

2
これは、マルチバイトのデータを壊滅的に爆破する可能性があります。
pguardiario 2013

158

ではなく、かなり高速な(そしてメモリ効率の良い)代替案を提案したいと思います。strtokpreg_split

$separator = "\r\n";
$line = strtok($subject, $separator);

while ($line !== false) {
    # do something with $line
    $line = strtok( $separator );
}

パフォーマンスをテストして、17000行のテストファイルを100回繰り返しpreg_splitました。27.7秒strtokかかりましたが、1.4秒かかりました。

けれどもことを注記$separatorとして定義され"\r\n"strtokとPHP4.1.0のように空行/トークンをスキップ-のいずれかの文字で区切ります。

strtokのマニュアルエントリを参照してください:http : //php.net/strtok


21
大きな行セットを処理するときのパフォーマンスを考慮して+1
CodeAngry 2013

4
この関数apiは完全に混乱しています(さまざまなパラメーターを使用して呼び出す)が、これが最良のソリューションです。どちらprey_splitexplode構造化文字列フラグメントを生じるために使用すべきではありません。バズーカでフライを狙うようなものです。
Maciej Sz 2014年

1
アプリの実行中にメモリ使用量を確認すると、魔法が見えます。それは実際にあなたがラインのそれぞれを通る場合、あなたループ内のメモリに読んでいるファイルを取り出し、そしてそれはあなたのトークンの位置を保持します。これをフラッシュして、本当にメモリ効率を上げる必要があります。php.net/strtok#103051
AbsoluteƵERØ

2
簡単なメモ、ループstrtok()内で何か他のものを使用すると、問題が発生しwhileます。私は、最初のスペースに文字列、最大でグラブすべてのもの(にそれを使用してもしstackoverflow.com/a/2477411/1767412)と私の計画通りに物事が起こっていなかった理由を実現するための分を取った
billynoah

1
受け入れられる答えである必要があります。おそらくすべてのオプションの中で最速のソリューションです。
ジョン

94

異なるシステムで改行を処理する必要がある場合は、PHPの事前定義された定数PHP_EOL(http://php.net/manual/en/reserved.constants.php)を使用し、explodeを使用して正規表現エンジンのオーバーヘッドを回避できます。 。

$lines = explode(PHP_EOL, $subject);

30
注意:さまざまなシステムで動作します、さまざまなシステムの文字列でうまく機能しません。PHPマニュアルの状態PHP_EOL (string)であるため、シンボル正しい「行の終わり」このプラットフォーム。
ワディム2013

@wadimは正しいです!UnixサーバーでWindowsテキストファイルを処理している場合、失敗します。
javsmo 14年

1
行の長さに応じて、これは大きな文字列に対して非常に大量のメモリを消費する可能性があることに注意してください。
Synchro

最後の行に行ターミネータが含まれている場合は、その後に別の空の文字列も返されることに注意してください。
18年

20

それは非常に複雑で醜いですが、私の意見ではこれは行く方法です:

$fp = fopen("php://memory", 'r+');
fputs($fp, $data);
rewind($fp);
while($line = fgets($fp)){
  // deal with $line
}
fclose($fp);

1
+1を使用php://tempして、より大きなデータを一時ディスクファイルに保存することもできます。
CodeAngry 2013

4
これにより、strtok()ソリューションとは異なり、空の行を検出できることに注意してください。ドキュメンテーションはphp.net/manual/en/…にあります
Josip Rodin

7
foreach(preg_split('~[\r\n]+~', $text) as $line){
    if(empty($line) or ctype_space($line)) continue; // skip only spaces
    // if(!strlen($line = trim($line))) continue; // or trim by force and skip empty
    // $line is trimmed and nice here so use it
}

^ これは行を適切に分割する方法であり、プラットフォームに互換性がありますRegexp:)


6

の潜在的なメモリの問題strtok

提案されたソリューションの1つはを使用しているためstrtok、残念ながら潜在的なメモリの問題は指摘されていません(ただし、メモリ効率が高いとされています)。マニュアルstrtokに従って使用する場合、

strtokへの最初の呼び出しだけが文字列引数を使用することに注意してください。それ以降のstrtokの呼び出しでは、現在の文字列のどこにあるかを追跡するため、使用するトークンのみが必要です。

これは、ファイルをメモリにロードすることによって行われます。大きなファイルを使用している場合、ファイルのループが完了したら、それらをフラッシュする必要があります。

<?php
function process($str) {
    $line = strtok($str, PHP_EOL);

    /*do something with the first line here...*/

    while ($line !== FALSE) {
        // get the next line
        $line = strtok(PHP_EOL);

        /*do something with the rest of the lines here...*/

    }
    //the bit that frees up memory
    strtok('', '');
}

物理ファイルのみに関心がある場合(例:データマイニング):

マニュアルよると、ファイルのアップロード部分にはfile次のコマンドを使用できます:

 //Create the array
 $lines = file( $some_file );

 foreach ( $lines as $line ) {
   //do something here.
 }

4

キリルの答えは、さまざまなマシンで改行を処理できるようにする必要があることを考慮すると最善です。

「私はたいてい、有用なPHP関数を探しています。それを行うためのアルゴリズムではありません。何か提案はありますか?」

私はこれらをたくさん使用します:

  • explode()を使用すると、単一の区切り文字を指定して、文字列を配列に分割できます。
  • implode()は、配列から文字列に戻るためのexplodeの対応物です。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.