相当于Linux的egrep在PHP中的文本文件目录?

时间:2013-09-06 11:21:49

标签: php linux

我想在PHP中搜索文本文件的目录,并列出发生字符串的所有实例。

在Linux中我会用这个:

egrep Cheese textfile_*.txt

PHP中是否有一个可以执行此操作的函数,而不会首先将所有内容附加到数组中?

3 个答案:

答案 0 :(得分:6)

一般案例

假设您希望解决方案在任意数量的任意大小的文件中匹配任意长度的字符串,并且您的系统资源是有限的。这很可能是场景,也是最棘手的。

你不能简单地将所有文件加载到内存中并将它们作为每个文件的一个大字符串进行搜索,因为这非常耗费内存,并且在功能复杂性方面实际上并不是特别有效(你循环所有文件,将它们加载到内存中,然后你再次循环它们并搜索内容。)

事实上,最好避免将整个文件加载到内存中 - 如果其中一个文件是10GB会怎样?

所以,首先,很明显,我们需要获取目录中的文件列表。有几种方法 - 我看到glob()提到了几次 - 但我会说这个算法的最佳方法是顺序读取条目并一次处理一个而不是加载整个列入数组然后迭代它,在PHP中意味着你要么需要opendir()函数族,要么需要DirectoryIterator迭代器类之一。许多人认为,在现代PHP中,后者是“正确”的方式。

现在您可以访问目录中的文件列表,您需要访问内容,而无需将整个文件加载到内存中。在PHP中,这意味着您将需要fopen()和(因为这是一个文本文件)fgets()。这允许我们一次处理一行文件,因此我们永远不会一次将多行数据加载到内存中。它还有一个允许您指定最大行长度的参数,如果文本文件由于某种原因包含很少/没有换行符,则应该使用这个参数。

所以我们以块的形式处理文件,我们只能strpos()搜索字符串的每个块,对吧?好吧,差不多。当搜索字符串跨越两个(或更多)块的边界时会发生什么?这是它开始变得有趣的地方,以及它开始变得值得寻找更复杂的字符串搜索算法(Boyer-Moore算法的变体可能在这里很好地为你服务)。

现在你唯一需要确定的是你想要匹配的具体程度 - 你想要区分大小写吗?你想原谅空白差异吗?你想标准化字符集吗?这些是在您实现字符串搜索算法之前必须回答和解决的问题。

情况的实际情况是,这些复杂性中的一些在PHP中解决起来相对较慢 - 如果你最终一次只能在字符串中迭代一个字符,例如,某些东西可以很快C,它将成为PHP中真正的性能杀手。

你的案例

......可能不需要这种复杂性。如果你知道你将永远处理少量相当小的文件,那么简单地做一个strpos(file_get_contents())组合方法很可能会很好 - 尽管你做了什么,一次处理一个文件而不是加载所有文件可能需要在执行任何搜索操作之前进入内存。


从本质上讲,你如何实现这一点取决于你正在使用的环境的几个因素 - 但是这样的事情在资源消耗方面可能非常危险,你必须考虑你的代码现在需要如何工作以及将来,然后才能构建正确的解决方案。

答案 1 :(得分:2)

  

PHP中是否有一个函数可以执行此操作而不会首先将所有内容附加到数组中?

不是一个特定的功能,但PHP有:

  • RecursiveDirectoryIterator
  • RecursiveIteratorIterator
  • RecursiveRegexIterator
  • GlobIterator
  • RegexIterator
  • SplFileInfo
  • SplFileObject

因为这些都是迭代器,当正确堆叠到彼此时,允许你遍历所有匹配的行。

对于你的具体情况,也许值得创建一个新的Iterator,它在其构造函数中使用类似于egrep的参数,并在其内部进行堆叠/处理,使其变得简单接口

或者可能是路径/文件/ SplFileInfo列表中的一个作为迭代器,也可以是一个用于遍历这些行以使其更流畅。

你也可以将Iterator-Garden中的一些泛型放在中间,就像DecoratingIterator的ExpandingIterator一样,将每个SplFileInfo转换为SplFileObject。

答案 2 :(得分:0)

使用 glob 功能:

$text_files = glob('directory/*.txt');

foreach($text_files as $text_file) {
   if (strpos('Cheese', file_get_contents($text_file)) !== false) {
      echo 'File ', $text_file, ' contains "Cheese"<br>';
   }
}