【问题标题】:Find all occurrences of list of strings in array inside a sentence, and replace everything except the first letter with dashes查找句子中数组中所有出现的字符串列表,并将除第一个字母之外的所有内容替换为破折号
【发布时间】:2018-08-02 14:45:57
【问题描述】:

我需要在一个句子中找到所有出现的字符串数组(原始 $list 有超过 780 个项目),并将除第一个字母之外的所有内容替换为 html 破折号。

这是我当前的代码:

function sanitize($string) {
    $list = array(
        "dumb",
        "stupid",
        "brainless"
    );

    # replace bad words
    $string = str_replace($list, '–', $string);
    return $string;
}

echo sanitize('hello, i think you are not intelligent, you are actually dumb and stupid.');

这是当前结果:

你好,我认为你并不聪明,你实际上是——而且——

结果应该是:

你好,我觉得你不聪明,你其实是d---和s------

关于如何解决这个问题的任何想法?谢谢!

【问题讨论】:

  • 你想要破折号的数量与原始字长相同 -1 还是只是一些静态的破折号?
  • @anubhava 是的,破折号的数量应该与原始 -1 相同

标签: php regex str-replace input-sanitization


【解决方案1】:

您可以通过\G 使用这种基于正则表达式的方法:

$str = 'hello, i think you are not intelligent, you are actually dumb and stupid.';
$list = array("dumb", "stupid", "brainless");

// use array_map to generate a regex of array for each word
$relist = array_map(function($s) { 
  return '/(?:\b(' . $s[0] . ')(?=' . substr($s, 1) . '\b)|(?!\A)\G)\pL/';
}, $list);

// call preg_replace using list of regex
echo preg_replace($relist, '$1-', $str) . "\n";

Code Demo

RegEx Demo

输出:

hello, i think you are not intelligent, you are actually d--- and s-----.

  • \G 断言位置在上一个匹配的结尾或第一个匹配的字符串的开头
  • (?!\A) 是负前瞻,以确保 \G 在行首不匹配

更新:

根据您下面的 cmets,您可以使用这种不同的方法:

$str = 'word';
$relist = array_map(function($s) { return '/\b' . $s . '\b/'; }, $list);

echo preg_replace_callback($relist, function($m) { 
   return '<span class="bad">' . $m[0][0] . str_repeat('-', strlen($m[0])-1) . '</span>';
}, $str);

输出:

first <span class="bad">w---</span>

【讨论】:

  • 我在 $list 数组中添加了 780 个单词(坏词),花费了这个时间:0.032 秒真实,0.032 秒墙,15 MB,46 个系统调用 - 如果有的话,在 cpu 方面听起来很昂贵每分钟数百次调用,但它仍然是最好的解决方案。
  • 我发现了一个小问题。如果我使用这个$string='word'; return preg_replace($relist, '&lt;span class="bad"&gt;$1-&lt;/span&gt;', $string);(我需要不同的颜色),结果是:&lt;span class="bad"&gt;w-&lt;/span&gt;&lt;span class="bad"&gt;-&lt;/span&gt;&lt;span class="bad"&gt;-&lt;/span&gt; - 结果应该是&lt;span class="bad"&gt;w---&lt;/span&gt; 有什么想法吗?
  • 查看我更新的答案以获取替代方法。
【解决方案2】:

您可以使用array_map 生成仅包含第一个字母的替换数组,并且可以选择为每个被替换的字符添加破折号:

function sanitize($string) {
    $list = array(
        "dumb",
        "stupid",
        "brainless"
    );

    $repl = array_map("dashReplace", $list);

    # replace bad words
    $string = str_replace($list, $repl, $string);
    return $string;
}

function dashReplace($str) {
    return $str{0}.str_repeat("-", strlen($str)-1);
}

echo sanitize('hello, i think you are not intelligent, you are actually dumb and stupid.');

您的示例的结果是:hello, i think you are not intelligent, you are actually d--- and s-----.

【讨论】:

  • 您的解决方案有效,我与@anubhava 的解决方案进行了比较,那个解决方案只是快了一点。也许正则表达式让它更快?
【解决方案3】:

您可以使用preg_replace_callback,但您需要为$list 数组中的每个项目添加反斜杠。

function sanitize($string) {
    $list = array(
        "/dumb/",
        "/stupid/",
        "/brainless/"
    );

    # replace bad words
    $string = preg_replace_callback($list,
        function ($matches) {
            return preg_replace('/\B./', '-', $matches[0]);
        }, 
        $string);
    return $string;
}

echo sanitize('hello, i think you are not intelligent, you are actually dumb and stupid.');

输出:

hello, i think you are not intelligent, you are actually d--- and s-----.

Code demo

【讨论】:

  • 它可以工作,但是在每个项目上添加一个斜线似乎是额外的工作(最终的 $list 数组有超过 780 个项目)。运行时间也比@anubhava 的解决方案要多。
  • @andufo 我同意你的观点,但是会出现诸如坏词ass 和单词*ass*ociation 之类的问题...我会添加反斜杠和单词边界\b 以提高准确性。
  • 这有点棘手。有些人还可以使用诸如asswiseassface 之类的组合......列表是无穷无尽的。因此,我将不得不牺牲 association 和其他类似的词。
猜你喜欢
  • 2015-07-26
  • 1970-01-01
  • 2018-05-07
  • 2021-12-27
  • 1970-01-01
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
相关资源
最近更新 更多