如何使用PHP在大量文件中搜索字符串

时间:2016-10-04 10:23:16

标签: php performance search

首先,我是PHP的新手,所以我不知道如何实现这一目标。我有一个文件夹,不断创建txt文件,大小和文本不等。我试图创建一些"搜索引擎"在用PHP编写的Linux系统上。到目前为止,我正在使用下面的代码。

if ( $_SERVER['REQUEST_METHOD'] == 'POST'){
    $path = '/example/files';
    $findThisString = $_POST['text_box'];
    $dir = dir($path);
    while (false !== ($file = $dir->read())){   
        if ($file != '.' && $file != '..'){
            if (is_file($path . '/' . $file)){
                $data = file_get_contents($path . '/' . $file);
                if (stripos($data, $findThisString) !== false){
                    echo '<p></p><font style="color:white; font-family:Arial">Found     Match - <a href="http://test.example.com/files/'. $file .'">'. $file .'</a>    <br>';
                }
            }
        }
    }
}
$dir->close();

现在这段代码很棒!但有一个问题,一旦文件夹获得大约40,000个文件,搜索需要花费大量时间来提取任何结果。现在我无法使用任何命令,例如greb。它必须用纯PHP编写,就像上面的代码一样。

是否有优化上述代码才能更快地运行?或者,我可以在PHP中使用更好的搜索功能吗?

2 个答案:

答案 0 :(得分:1)

脚本速度如此之慢的原因有很多,而且为了减少所需的时间,您需要做的就是完全取决于代码的确切部分会导致速度减慢。
这意味着您需要通过分析器放置代码,然后调整它报告的代码部分是原因。没有分析器,我们所能做的就是猜测。不一定正确。

正如您对问题的评论所述,使用已经制作的搜索引擎将是更好的解决方案。特别是为此类目的而制作的东西,因为它会大大缩短的时间
即使是用于Linux shell的内置grep命令也是一种改进。

那就是说,我怀疑你的代码速度太慢的原因是因为你正在阅读和搜索PHP中所有文件的内容。 stripos()在这里特别容易受到怀疑,因为这是一个相当慢的搜索 另一个因素可能是循环中的read()调用,因为我相信它们会在每次调用时执行IO操作。此外,在脚本中对echo进行大量调用可能会导致速度变慢,具体取决于您拥有的数量。几百个不是很明显,但有几千个。

考虑到这些最后几点,以及我建议您更容易维护代码的一些其他常规更改,我对您的代码进行了以下更改。

<?php

if (isset ($_POST['text_box'])) {
    $path = '/example/files';
    $result = search_files ($_POST['text_box'], $path);
}

/**
 * Searches through the files in the given path, for the search term.
 *
 * @param string $term The term to search for, only "word characters" as defined by RegExp allowed.
 * @param string $path The path which contains the files to be searched.
 * 
 * @return string Either a list of links to the files, or an error message.
 */
function search_files ($term, $path) {
    // Ensuring that we have a closing slash at the end of the path, so that
    // we can add a file-descriptor for glob() to use.
    if (substr ($path, -1) != '/') {
        $path .= '/';
    }

    // If we don't have a valid/readable path we ened to throw an error now.
    // This only happens if the code itself is wrong, as it's not user-supplied,
    // thus an exception is thrown.
    if (!is_dir ($path) || !is_readable ($path)) {
        throw new InvalidArgumentException ("Not a valid search path!");
    }

    // This should be validated to ensure you get sane input,
    // in order to avoid erroneous responses to the user and
    // possible attacks.
    // Addded a simple test to ensure we only accept "word characters".
    if (!preg_match ('/^\w+\\z/', $term)) {
        // Invalid input. Show warning to user.
        return 'Not a valid search string.';
    }

    // Using glob so that we retrieve a list of all files in one operation.
    $contents = glob ($path.'*');

    // Using a holding variable, as this many echo statements take
    // noticable longer time than just concatenating strings and
    // echoing it out once.
    $output = '';

    // Using printf() templates to make the code easier to reach.
    // Ideally the HTML-code shouldn't be in this string either, but adding
    // a templating system is far beyond the reach of this Q&A.
    $outTemplate = '<p class="found">Found Match - <a href="http://test.example.com/files/%1$s">%2$s</a></p>';

    foreach ($contents as $file) {
        // Skip the hardlinks for parent and current folder.
        if ($file == '.' || $file == '..') {
            continue;
        }

        // Skip if the path isn't a file.
        if (!is_file ($path . '/' . $file)) {
            continue;
        }

        // This one is the big issue. Reading all of the files one by one will take time!
        $data = file_get_contents ($path . '/' . $file);

        // Same with running a case-insensitive search!
        if (stripos ($data, $term) !== false) {
            // Added output escaping to prevent issues with possible meta-characters.
            // (A problem also known as XSS attacks)
            $output .= sprintf ($outTemplate, htmlspecialchars (rawurlencode($file)), htmlspecialchars($file));
        }
    }

    // Lastly, if the output string is empty we haven't found anything.
    if (empty($output)) {
        return "Term not found";
    }

    return $output;
}

答案 1 :(得分:0)

如果你有两种方法,你不能使用linux命令: 1)它是在数据库中保存文件,之后,当你需要找到你从数据库调用查询搜索文件时。 2)它创建一个索引文件(将保存在他列表文件中的文件)

1种和2种方式可以帮助您节省执行脚本的时间。对于更新文件,您可以编写Cron任务,该任务将开始导入数据库或文件中的新文件。