【问题标题】:RegExp PHP show the smallest matchRegExp PHP 显示最小匹配
【发布时间】:2017-07-10 05:31:48
【问题描述】:

我正在尝试在 php 中获取正则表达式的最小匹配。

例如: 如果我有电视和电话这两个词,而用户输入的是 tel,我的正则表达式应该返回最小的词,在这种情况下是电话。 简而言之,我想做一个搜索脚本。但是对于用户输入中缺少的字母,我使用这个t[a-zA-Z0-9]{0,2}l[a-zA-Z0-9]{0,},所以我的最后一个字母形式的单词后面会跟着 N 个字符。

我的问题是:我该如何做我的正则表达式来显示最小的单词。

【问题讨论】:

  • 正则表达式本身无法做到这一点。您必须使用正则表达式找到所有匹配项,然后按长度对它们进行排序并显示第一个。
  • 您可能需要一些代码,例如您要搜索的内容以及搜索方式。

标签: php regex algorithm


【解决方案1】:

很遗憾,你做不到。正则表达式可以匹配你想要的,但它不提供任何函数来比较子匹配。您必须匹配整个字符串,并在您的情况下通过 PHP 代码比较子匹配项。

// your array of matched words
$words = array(...);

$foundWordLength = null;
$foundWord = '';

foreach ($words as $word) {
   if (strlen($word) < $foundWordLength || $foundWordLength === null) {
      $wordLength = strlen($word);
      $foundWord = $word;
   }
}

echo $foundWord;

【讨论】:

    【解决方案2】:

    我认为您可以使用正则表达式实现它的唯一方法是首先按理想的顺序对单词进行排序,在您的情况下从最短到最长。

    然后,如果您的单词数量相对较少,为了性能起见,可以将单词连接起来并同时检查第一个匹配项。这是可能的,因为 PHP RegExp 实现从左到右执行搜索。请参阅下面的示例中的函数search_short()

    无论如何,循环和检查从最低的单词开始也可以。在下面的示例中检查函数search_long()

    <?php
    $given = [
        'telephone',
        'television',
    ];
    // NB: Do not forget to sanitize user input, i.e. $query
    echo (search_short($given, 'tele') ?: 'Nothing found') . PHP_EOL;
    echo (search_long($given, 'tele') ?: 'Nothing found') . PHP_EOL;
    echo (search_short($given, 't[a-zA-Z0-9]{0,2}l[a-zA-Z0-9]{0,}') ?: 'Nothing found') . PHP_EOL;
    echo (search_long($given, 't[a-zA-Z0-9]{0,2}l[a-zA-Z0-9]{0,}') ?: 'Nothing found') . PHP_EOL;
    
    /**
     * @param string[] $given
     * @param string   $query
     *
     * @return null|string
     */
    function search_short($given, $query)
    {
    
        // precalculating the length of each word, removing duplicates, sorting
        $given = array_map(function ($word) {
            return mb_strlen($word); // `mb_strlen()` is O(N) function, while `strlen()` is O(1)
        }, array_combine($given, $given));
        asort($given);
    
        // preparing the index string
        $index = implode(PHP_EOL, array_keys($given));
        // and, finally, searching (the multiline flag is set)
        preg_match(
            sprintf('/^(?<word>%s\w*)$/mu', $query), // injecting the query word
            $index,
            $matches
        );
    
        // the final pattern looks like: "/^(?P<word>tele\w*)$/mui"
        if (array_key_exists('word', $matches)) {
            return $matches['word'];
        }
        return null;
    }
    
    /**
     * @param string[] $given
     * @param string   $query
     *
     * @return null|string
     */
    function search_long($given, $query)
    {
        $pattern = sprintf('/^(?<word>%s\w*)$/u', $query);
    
        // precalculating the length of each word, removing duplicates, sorting
        $given = array_map(function ($word) {
            return mb_strlen($word);
        }, array_combine($given, $given));
        asort($given);
    
    
        foreach ($given as $word => $count) {
            if (preg_match($pattern, $word, $matches)) {
                if (array_key_exists('word', $matches)) {
                    return $matches['word'];
                }
            }
        }
        return false;
    }
    

    当然,它不是最有效的算法,可以通过多种方式进行改进。但是为了完成这个需要更多关于范围和使用的信息。

    【讨论】:

      【解决方案3】:

      正则表达式引擎通常既没有用于存储复杂条件的预期内存,也没有从提供复杂比较的编程语言功能中受益。

      如果标记 不是漫无目的,你可以多写几行来完成你的工作。

      $str = 'television and telephone';
      preg_match_all('/tel\w*/', $str, $matches);
      usort($matches[0], function($a, $b) {
          return strlen($a) <=> strlen($b);
      });
      echo $matches[0][0];
      

      【讨论】:

      • @woo 感谢您的回答。这对我帮助很大 。干杯!
      猜你喜欢
      • 1970-01-01
      • 2017-02-11
      • 1970-01-01
      • 2011-08-03
      • 2016-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-29
      相关资源
      最近更新 更多