在 20mb 平面文件数据库中搜索整个单词的最快方法 (PHP)

Fastest way to search for whole words in 20mb flat file database (PHP)

我有 20MB 的平面文件数据库,大约 500k 行,只允许 [a-z0-9-] 个字符,一行平均 7 个单词,没有空行或重复行:

平面文件数据库:

put-returns-between-paragraphs
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces

我正在搜索 whole words only 并从此数据库中提取 first 10k results

到目前为止,如果在数据库的前 20k 行中找到 10k 个匹配项,则此代码工作正常,但如果单词很少见,则脚本必须搜索所有 500k 行,这会慢 10 倍。

设置:

$cats = file("cats.txt", FILE_IGNORE_NEW_LINES);
$search = "end";
$limit = 10000;

搜索:

foreach($cats as $cat) {
    if(preg_match("/\b$search\b/", $cat)) {
        $cats_found[] = $cat;
        if(isset($cats_found[$limit])) break;
    }
}

我的php技能和知识有限,我不会也不知道如何使用sql,所以这是我能做到的最好的,但我需要一些建议:

感谢阅读本文,抱歉英语不好,这是我的第三语言。

虽然您可能 运行 内存不足,但这对您逐行阅读很有用:

( might need to tweak your php.ini memory_limit and max_execution_time or run via cli )

$rFile = fopen( 'inputfile.txt', 'r' );
$iLineNumber = 0;
$sSearch = '123';
$iLimit  = 5000;
while( !feof( $rFile ) )
{
    if( $iLineNumber > $iLimit )
    {
        break;
    }
    $sLine = fgets( $rFile );
    if( preg_match("/\b$sSearch\b/", $sLine, $aMatches ) ) 
    {
        $aCats[] = $aMatches[ 0 ];
    }
    ++$iLineNumber;
}
var_dump( $aCats );

我的建议是 将文件重新格式化为 sql 导入 并使用数据库。平面文件搜索速度明显变慢。

Infile:

put-returns-between-paragraphs
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces
put-returns-between-paragraphs
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces
put-returns-between-paragraphs
123
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces
put-returns-between-paragraphs
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces
123
put-returns-between-paragraphs
for-linebreak-add-2-spaces-at-end
indent-code-by-4-spaces-indent-code-by-4-spaces

Output:

array(2) {
  [0]=>
  string(3) "123"
  [1]=>
  string(3) "123"
}

它从匹配项中包装了一个额外的数组,因此我们必须使用 [0]

如果 大多数 行不包含搜索到的词,您可以减少执行 preg_match() 的频率,如下所示:

foreach ($lines as $line) {
    // fast prefilter...
    if (strpos($line, $word) === false) {
        continue;
    }
    // ... then proper search if the line passed the prefilter
    if (preg_match("/\b{$word}\b/", $line)) {
        // found
    }
}

不过,在实际情况下需要对标。