遍历PHP中字符串中的每一行
Posted
技术标签:
【中文标题】遍历PHP中字符串中的每一行【英文标题】:Iterate over each line in a string in PHP 【发布时间】:2010-11-30 13:38:00 【问题描述】:我有一个表单,允许用户上传文本文件或将文件内容复制/粘贴到文本区域。我可以轻松区分这两者并将他们输入的任何一个放入字符串变量中,但我该从哪里开始呢?
我需要遍历字符串的每一行(最好不要担心不同机器上的换行符),确保它只有一个标记(没有空格、制表符、逗号等),清理数据,然后生成基于所有行的 SQL 查询。
我是一个相当优秀的程序员,所以我知道如何做到这一点的一般想法,但是自从我使用 php 以来已经很长时间了,我觉得我正在寻找错误的东西,因此提出了无用的信息.我遇到的关键问题是我想逐行读取字符串的内容。如果它是一个文件,那就很容易了。
我主要在寻找有用的 PHP 函数,而不是如何做到这一点的算法。有什么建议吗?
【问题讨论】:
您可能需要先规范化换行符。s($myString)->normalizeLineEndings()
方法可用于 github.com/delight-im/PHP-Str(MIT 许可下的库),它有许多其他有用的字符串助手。你可能想看看源代码。
【参考方案1】:
preg_split
包含文本的变量,并遍历返回的数组:
foreach(preg_split("/((\r?\n)|(\r\n?))/", $subject) as $line)
// do stuff with $line
【讨论】:
除了 \n\r 之外还会处理 ^M 吗? 我不确定 ascii 回车是否会在放入变量后转换为 \r。如果不是,您始终可以使用带有 ascii 值的 split()/exlope() 代替 -- ch(13) 一个更好的正则表达式是/((\r?\n)|(\r\n?))/
。
为了匹配 Unix LF (\n), MacOS/((\r?\n)|(\n?\r))/
这对于多字节数据可能会造成灾难性的轰炸。【参考方案2】:
考虑到您需要能够在不同的机器上处理换行符,Kyril 的回答是最好的。
“我主要是在寻找有用的 PHP 函数,而不是一个算法 去做吧。有什么建议吗?”
我经常使用这些:
explode() 可用于将字符串拆分为数组,给定一个 单个分隔符。 implode() 是explode 的对应物,从数组返回到字符串。【讨论】:
【参考方案3】:如果您需要在不同系统中处理换行符,您可以简单地使用 PHP 预定义常量 PHP_EOL (http://php.net/manual/en/reserved.constants.php) 并简单地使用 explode 来避免开销正则表达式引擎。
$lines = explode(PHP_EOL, $subject);
【讨论】:
注意:它可以在不同的系统上工作,但它不能很好地用于来自不同系统的字符串。 PHP Manual 声明PHP_EOL (string)
是 此 平台的正确“行尾”符号。
@wadim 是对的!如果您在 Unix 服务器上处理 Windows 文本文件,它将失败。
请注意,根据行的长度,这可能会为大字符串占用大量内存。
请注意,如果最后一行包含行终止符,那么这之后还会返回另一个空字符串。【参考方案4】:
我想提出一个明显更快(并且内存效率更高)的替代方案:strtok
而不是preg_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
【讨论】:
+1 用于性能考虑 处理大型行集时。 虽然这个函数api是一团糟(用不同的参数调用)这是最好的解决方案。prey_split
和 explode
都不应该用于生成结构化字符串片段。这就像用火箭筒瞄准苍蝇。
如果您在应用程序运行时检查内存使用情况,那么您将看到神奇之处。如果您循环遍历每一行,它实际上会将您正在读取的文件拉入内存,并且它会保留您的令牌位置。您需要刷新它以真正提高内存效率。 php.net/strtok#103051
快速说明,在 while
循环内的其他东西上使用 strtok()
会破坏事情。我还用它来抓取字符串中的所有内容,直到第一个空格 (***.com/a/2477411/1767412) 并花了我一分钟才意识到为什么事情没有按计划进行
应该是公认的答案,可能是所有选项中最快的解决方案。【参考方案5】:
它过于复杂和丑陋,但我认为这是要走的路:
$fp = fopen("php://memory", 'r+');
fputs($fp, $data);
rewind($fp);
while($line = fgets($fp))
// deal with $line
fclose($fp);
【讨论】:
+1 也可以使用php://temp
将较大的数据存储到临时磁盘文件中。
应该注意的是,这允许您检测空行,这与 strtok() 解决方案不同。文档位于php.net/manual/en/…【参考方案6】:
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
:)
【讨论】:
【参考方案7】:
strtok
的潜在内存问题:
由于建议的解决方案之一使用strtok
,不幸的是,它没有指出潜在的内存问题(尽管它声称内存高效)。根据the manual使用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('', '');
如果您只关心物理文件(例如数据挖掘):
According to the manual,文件上传部分可以使用file
命令:
//Create the array
$lines = file( $some_file );
foreach ( $lines as $line )
//do something here.
【讨论】:
【参考方案8】:与@pguardiario 类似,但使用更“现代”(OOP)的界面:
$fileObject = new \SplFileObject('php://memory', 'r+');
$fileObject->fwrite($content);
$fileObject->rewind();
while ($fileObject->valid())
$line = $fileObject->current();
$fileObject->next();
SplFileObject 文档:https://www.php.net/manual/en/class.splfileobject.php
PHP IO 流:https://www.php.net/manual/en/wrappers.php.php
【讨论】:
以上是关于遍历PHP中字符串中的每一行的主要内容,如果未能解决你的问题,请参考以下文章
在Python中的数据框中的每一行的两个子字符串之间选择字符串
如何使用 Windows 批处理文件遍历文本文件中的每一行?
遍历 CSV 的每一行并使用 Python-Pandas 写入文件夹中的单个 JSON 文件
在 pyspark 的 StructStreaming 中;如何将 DataFrame 中的每一行(json 格式的字符串)转换为多列