【问题标题】:Google-like search query tokenization & string splitting类似于 Google 的搜索查询标记化和字符串拆分
【发布时间】:2010-12-25 09:17:47
【问题描述】:

我希望对搜索查询进行标记,类似于 Google 的做法。例如,如果我有以下搜索查询:

the quick "brown fox" jumps over the "lazy dog"

我想要一个带有以下标记的字符串数组:

the
quick
brown fox
jumps
over
the
lazy dog

如您所见,标记用双引号保留空格。

我正在寻找一些如何在 C# 中执行此操作的示例,最好不要使用正则表达式,但是如果这样最有意义并且性能最好,那就这样吧。

我还想知道如何扩展它以处理其他特殊字符,例如,在术语前面放置一个 - 以强制从搜索查询中排除等等。

【问题讨论】:

  • 在您的语法中,双引号字符 (") 是否可以在除表示多字标记之外的任何其他地方使用?
  • 就我而言,不,它不能。

标签: c# search tokenize


【解决方案1】:

到目前为止,这看起来是 RegEx 的一个很好的候选者。如果它变得更加复杂,那么可能需要更复杂的标记化方案,但除非必要,否则您应该避免使用该路线,因为它的工作量要大得多。 (另一方面,对于复杂的模式,正则表达式很快就会变成狗,同样应该避免)。

这个正则表达式应该可以解决你的问题:

("[^"]+"|\w+)\s*

这是一个 C# 用法示例:

string data = "the quick \"brown fox\" jumps over the \"lazy dog\"";
string pattern = @"(""[^""]+""|\w+)\s*";

MatchCollection mc = Regex.Matches(data, pattern);
foreach(Match m in mc)
{
    string group = m.Groups[0].Value;
}

这种方法的真正好处是它可以很容易地扩展以包含您的“-”要求,如下所示:

string data = "the quick \"brown fox\" jumps over " +
              "the \"lazy dog\" -\"lazy cat\" -energetic";
string pattern = @"(-""[^""]+""|""[^""]+""|-\w+|\w+)\s*";

MatchCollection mc = Regex.Matches(data, pattern);
foreach(Match m in mc)
{
    string group = m.Groups[0].Value;
}

现在我和其他人一样讨厌阅读正则表达式,但如果你把它分开,这个很容易阅读:

(
-"[^"]+"
|
"[^"]+"
|
-\w+
|
\w+
)\s*

说明

  1. 如果可能,匹配一个减号,后跟一个“,然后是所有内容,直到下一个”
  2. 否则匹配 " 后跟所有内容直到下一个 "
  3. 否则匹配 a - 后跟任何单词字符
  4. 否则匹配尽可能多的单词字符
  5. 将结果放入一个组中
  6. 吞下后面的所有空格字符

【讨论】:

    【解决方案2】:

    像这样逐个字符地转到字符串:(某种伪代码)

    array words = {} // empty array
    string word = "" // empty word
    bool in_quotes = false
    for char c in search string:
        if in_quotes:
            if c is '"':
                append word to words
                word = "" // empty word
                in_quotes = false
            else:
                append c to word
       else if c is '"':
            in_quotes = true
       else if c is ' ': // space
           if not empty word:
               append word to words
               word = "" // empty word
       else:
            append c to word
    
    // Rest
    if not empty word:
        append word to words
    

    【讨论】:

    • 如果正则表达式不够,我认为这大致是我的想法。但是,我非常强烈建议不要使用字符串。由于字符串的不变性,您将像 mad 一样分配字符串。最好让 word 成为一个字符串构建器,甚至只是一个字符数组。
    • 你是对的。但这是伪代码。这是关于原则的。
    【解决方案3】:

    几天前我只是想弄清楚如何做到这一点。我最终使用了 Microsoft.VisualBasic.FileIO.TextFieldParser,它完全符合我的要求(只需将 HasFieldsEnclosedInQuotes 设置为 true)。当然,在 C# 程序中包含“Microsoft.VisualBasic”看起来有些奇怪,但它确实有效,而且据我所知,它是 .NET 框架的一部分。

    为了将我的字符串放入 TextFieldParser 的流中,我使用了“new MemoryStream(new ASCIIEncoding().GetBytes(stringvar))”。不确定这是否是最好的方法。

    编辑:我认为这不能满足您的“-”要求,所以也许 RegEx 解决方案更好

    【讨论】:

      【解决方案4】:

      我一直在寻找这个问题的 Java 解决方案,并使用@Michael La Voie 提出了一个解决方案。尽管在 C# 中提出了问题,但我想我会在这里分享它。希望没关系。

      public static final List<String> convertQueryToWords(String q) {
          List<String> words = new ArrayList<>();
          Pattern pattern = Pattern.compile("(\"[^\"]+\"|\\w+)\\s*");
          Matcher matcher = pattern.matcher(q);
          while (matcher.find()) {
              MatchResult result = matcher.toMatchResult();
              if (result != null && result.group() != null) {
                  if (result.group().contains("\"")) {
                      words.add(result.group().trim().replaceAll("\"", "").trim());
                  } else {
                      words.add(result.group().trim());
                  }
              }
          }
          return words;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-17
        • 2018-09-15
        相关资源
        最近更新 更多