在PHP中迭代字符串中的每一行

Top*_*gio 117 php string

我有一个表单,允许用户上传文本文件或将文件的内容复制/粘贴到textarea.我可以很容易地区分这两者并将他们输入的任何一个放入一个字符串变量,但是我从那里去哪里?

我需要遍历字符串的每一行(最好不要担心不同机器上的换行符),确保它只有一个令牌(没有空格,制表符,逗号等),清理数据,然后生成SQL查询基于所有线条.

我是一个相当优秀的程序员,所以我知道如何做到的一般想法,但是我用PHP工作已经很久了,我觉得我在寻找错误的东西,从而得出无用的信息.我遇到的关键问题是我想逐行读取字符串的内容.如果它是一个文件,那将很容易.

我主要寻找有用的PHP函数,而不是如何做到的算法.有什么建议?

Kyr*_*ril 170

preg_split 包含文本的变量,并遍历返回的数组:

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

  • 更好的正则表达式是`/((\ r?\n)|(\ r \n?n))/`. (11认同)
  • 要匹配Unix LF(\n),MacOS <9 CR(\ r),Windows CR + LF(\ r \n)和稀有LF + CR(\n\r),它应该是:`/((\ r? \n)的?|(\n\r))/` (3认同)
  • 这可能会对多字节数据造成灾难性的轰炸. (2认同)

Erw*_*els 141

我想提出一个明显更快(和内存效率)的替代方案:strtok而不是preg_split.

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

while ($line !== false) {
    # do something with $line
    $line = strtok( $separator );
}
Run Code Online (Sandbox Code Playgroud)

测试性能,我在17000行测试文件上迭代100次:preg_split花了27.7秒,而strtok花了1.4秒.

请注意,虽然$separator定义为"\r\n",strtok将在任一字符上分开 - 从PHP4.1.0开始,跳过空行/标记.

请参阅strtok手册条目:http: //php.net/strtok

  • **处理大型线路时的性能考虑因素**+ 1. (17认同)
  • 虽然这个函数api是一个完全混乱(使用不同参数调用),但这是最好的解决方案.不应该使用`prey_split`或`explode`来产生结构化的字符串片段.就像**瞄准用火箭筒**飞行的**. (4认同)
  • 如果您在应用程序运行时检查内存使用情况,您就会看到神奇的效果。如果您循环遍历每一行,它实际上会将您正在读取的文件拉入内存,*并且*它会保留您的令牌位置。您需要刷新它才能真正提高内存效率。http://php.net/strtok#103051 (3认同)
  • 快速注意,在`while`循环内的其他内容上使用`strtok()`会破坏事物.我也用它来抓住第一个空间中的所有字符串(/sf/answers/173418801/)并花了一分钟时间来了解为什么事情没有按计划进行 (2认同)
  • 应该是公认的答案,可能是所有选项中最快的解决方案。 (2认同)

Fer*_*rCa 94

如果你需要处理不同系统中的换行,你可以简单地使用PHP预定义常量PHP_EOL(http://php.net/manual/en/reserved.constants.php)并简单地使用explode来避免正则表达式引擎的开销.

$lines = explode(PHP_EOL, $subject);
Run Code Online (Sandbox Code Playgroud)

  • 注意:它将在不同的系统*上工作*但它不适用于来自不同系统*的字符串*.[PHP手册](http://php.net/manual/en/reserved.constants.php)声明`PHP_EOL(string)`是**这个**平台的正确'行尾'符号._ (29认同)

pgu*_*rio 18

这是过于复杂和丑陋,但在我看来这是要走的路:

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

  • 应该注意的是,与strtok()解决方案不同,这允许您检测空行.该文档位于http://php.net/manual/en/wrappers.php.php#refsect2-wrappers.php-unknown-unknown-unknown-unknown-unknown-descriptios (4认同)
  • +1,您还可以使用 `php://temp` 将较大的数据存储到临时磁盘文件。 (2认同)

Abs*_*ERØ 7

潜在的内存问题strtok

由于建议的解决方案之一使用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('', '');
}
Run Code Online (Sandbox Code Playgroud)

如果您只关心物理文件(例如数据挖掘):

根据手册,对于文件上传部分,您可以使用以下file命令:

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

 foreach ( $lines as $line ) {
   //do something here.
 }
Run Code Online (Sandbox Code Playgroud)


Joe*_*ley 5

考虑到您需要能够在不同的机器上处理换行符,基里尔的答案是最好的。

“我主要是在寻找有用的 PHP 函数,而不是如何实现它的算法。有什么建议吗?”

我经常使用这些:


Cod*_*gry 5

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
}
Run Code Online (Sandbox Code Playgroud)

^ 这是您正确断行的方式,跨平台兼容Regexp:)