首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 關鍵詞過濾:基於 strpos 的高性能實現與文件編碼避坑指南

     編輯:PHP 相關知識

strpos 基礎

strpos(string $haystack, string $needle, int $offset = 0): int|false 返回 $needle 在 $haystack 中首次出現的位置(從 0 開始),未找到返回 false。區分大小寫,不區分大小寫請用 stripos()。

完整實現:從文本文件加載關鍵詞

<?php
/**
 * 關鍵詞過濾器(strpos 版)
 * 支持從文件加載,自動處理 BOM、換行符、空白字符
 */
class KeywordFilter {
    private $keywords = [];

    /**
     * @param string $filePath 關鍵詞文件路徑,每行一個詞
     * @param bool   $caseInsensitive 是否不區分大小寫
     */
    public function __construct(string $filePath, bool $caseInsensitive = false) {
        $this->loadFromFile($filePath);
        $this->caseInsensitive = $caseInsensitive;
    }

    private function loadFromFile(string $path) {
        if (!is_file($path) || !is_readable($path)) {
            throw new InvalidArgumentException("關鍵詞文件不可讀:$path");
        }
        $content = file_get_contents($path);
        // 移除 BOM
        $content = preg_replace('/^\xEF\xBB\xBF/', '', $content);
        // 統一換行符
        $content = str_replace(["\r\n", "\r"], "\n", $content);
        // 按行分割,過濾空行並 trim
        $this->keywords = array_filter(array_map('trim', explode("\n", $content)), function($w) {
            return $w !== '';
        });
    }

    /**
     * 檢測文本是否包含關鍵詞
     * @param string $text
     * @return string|false 匹配到的關鍵詞,未命中返回 false
     */
    public function check(string $text) {
        $func = $this->caseInsensitive ? 'stripos' : 'strpos';
        foreach ($this->keywords as $kw) {
            // 關鍵:每次比對前 trim,防止文件行尾空格導致誤判
            $kw = trim($kw);
            if ($kw === '') continue;
            if ($func($text, $kw) !== false) {
                return $kw; // 返回首個命中的詞
            }
        }
        return false;
    }

    /**
     * 替換所有關鍵詞為指定字符
     * @param string $text
     * @param string $replacement
     * @return string
     */
    public function replace(string $text, string $replacement = '***') {
        $func = $this->caseInsensitive ? 'stripos' : 'strpos';
        foreach ($this->keywords as $kw) {
            $kw = trim($kw);
            if ($kw === '') continue;
            // 使用 preg_replace 實現不區分大小寫替換
            if ($this->caseInsensitive) {
                $text = preg_replace('/' . preg_quote($kw, '/') . '/i', $replacement, $text);
            } else {
                $text = str_replace($kw, $replacement, $text);
            }
        }
        return $text;
    }
}

// === 使用示例 ===
// keywords.txt 內容(UTF-8 無 BOM):
// 關鍵字1
// 關鍵字2
// 敏感詞

$filter = new KeywordFilter(__DIR__ . '/keywords.txt', true); // 不區分大小寫

$content = '這裡是要發布的內容,含有關鍵字2';
$hit = $filter->check($content);

if ($hit) {
    echo "檢測到關鍵詞:$hit\n";
    echo "凈化後:" . $filter->replace($content) . "\n";
} else {
    echo "通過關鍵詞檢測\n";
}

文件編碼避坑實錄

  1. 現象:代碼邏輯正確,但讀取文件匹配失敗
  2. 排查:print_r($keywords) 顯示數組正常,但 var_dump($keywords[0]) 發現字符串長度比可見字符多
  3. 原因:文本文件保存為 UTF-8 **with BOM** 或包含 **\r\n** 換行符,導致 explode("\n") 後每行尾部殘留 \r 或 BOM 字符
  4. 修復:trim() 每行關鍵詞,或統一轉換文件編碼為 **UTF-8 無 BOM**、**LF 換行**

性能對比

方法時間複雜度適用場景
strpos 遍歷O(n × m)關鍵詞 < 500,文本 < 1KB
單正則合並 /(kw1|kw2|kw3)/O(n) 正則引擎優化關鍵詞 < 2000,需替換操作
Aho-Corasick 自動機 (DFA)O(n + m + z)關鍵詞 > 5000,高頻檢測,最長匹配

擴展:支持通配符與正則關鍵詞

// 關鍵詞文件格式擴展:每行「類型:模式」
// 普通:敏感詞
// 正則:\d{11}  (匹配手機號)
// 通配:傻*瓜   (內部轉換為 正則 傻.*?瓜)

public function loadFromFile(string $path) {
    // ... 讀取文件 ...
    foreach ($lines as $line) {
        $line = trim($line);
        if (!$line || $line[0] === '#') continue; // 支持註釋
        if (strpos($line, ':') !== false) {
            list($type, $pattern) = explode(':', $line, 2);
            $this->keywords[$type][] = $pattern;
        } else {
            $this->keywords['plain'][] = $line;
        }
    }
}

通過 trim() 消除隱形字符干擾,結合 stripos 實現不區分大小寫檢測,是中小型項目關鍵詞過濾的輕量級首選。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved