正则表达式:如何从字符串中获取单词(C#)

16

我的输入由用户发布的字符串组成。

我想要做的是创建一个包含单词及其使用频率的字典。这意味着我需要解析一个字符串,清除所有垃圾,并输出一个单词列表。

例如,假设输入为"#@!@LOLOLOL YOU'VE BEEN \***PWN3D*** ! :') !!!1einszwei drei !"

我需要的输出是以下列表:

  • "LOLOLOL"
  • "YOU'VE"
  • "BEEN"
  • "PWN3D"
  • "einszwei"
  • "drei"

我不太擅长正则表达式,一直在搜索,但我的谷歌功夫似乎不够好...

我该如何从输入得到所需的输出?


3
好的,我会尽力进行翻译。以下是需要翻译的网站内容:正则表达式.info正则表达式是一种强大的文本处理工具。它们被广泛应用于各种编程语言和编辑器,用于查找和操作文本中的模式。这个网站旨在成为你学习和使用正则表达式的最佳资源。我们提供了全面的教程,涵盖了从基础知识到高级主题的所有内容。我们还提供了一个交互式正则表达式测试器和大量的实用信息,以帮助您快速入门并掌握这项技能。无论您是初学者还是有经验的开发人员,我们相信您都会从这个网站中获得价值。让我们开始吧! - Jason
6个回答

24

简单的正则表达式:

\w+

这个匹配一串“单词”字符,接近你想要的结果。

这个稍微更准确一些:

\w(?<!\d)[\w'-]*

它匹配任意数量的单词字符,并确保第一个字符不是数字。

这里是我的匹配结果:

1 LOLOLOL
2 YOU'VE
3 BEEN
4 PWN3D
5 einszwei
6 drei

现在,这更像是你想要的了。

编辑:
使用否定回顾的原因是,一些正则表达式语言支持Unicode字符。使用 [a-zA-Z] 会错过许多想要的“单词”字符。允许 \w 并禁止 \d 包括了所有可能在文本块中作为单词开头的Unicode字符。

编辑 2:
我发现了一种更简洁的方法来实现否定回顾的效果:双重否定字符类和单一否定排除。

[^\W\d][\w'-]*(?<=\w)

这与上面的方法相同,唯一的区别是它还确保单词以单词字符结尾。最后,还有:

[^\W\d](\w|[-']{1,2}(?=\w))*

确保没有超过两个非单词字符连续出现。也就是说,它匹配"word-up"但不匹配"word--up",这是合理的。如果你想要它匹配"word--up",但不匹配"word---up",你可以将 2 改为 3


@Led:你可能想看一下第二次编辑末尾的正则表达式。它可能更接近你所寻找的内容。 - John Gietzen
被踩。带有 ' 符号的单词被分成了几个部分。 - Vyachaslav Gerchicov

6
你应该研究自然语言处理(NLP),而不是正则表达式。如果你的目标语言不止一种,那么你需要考虑这一点。由于你正在使用C#,可以查看SharpNLP项目。 编辑:只有在你关心尝试分割的单词的语义内容时,才需要采用这种方法。

1
非常感谢您的回复! :) 但是让我们保持简单,假设我不关心语言 - 我只考虑带有可选的'''和/或'-'字符的单词。 - Led
如果您不关心语言,那么为什么不直接使用string.Replace()替换掉您不想要的所有字符,然后在空格字符上使用string.Split()进行分割呢?无需使用正则表达式。 - Mike Atlas
1
@Mike - 哈哈,那就是我刚刚建议的。 - Jason
因为指定我允许的内容更自然:带有可选的 ' 或 - 字符的单词,且没有其他内容。 如果我做错了,请告诉我,因为这对我来说还比较新奇 ;) - Led
有多种方法可以解决问题。 - Mike Atlas

2

使用以下代码:

var pattern = new Regex(
  @"( [^\W_\d]              # starting with a letter
                            # followed by a run of either...
      ( [^\W_\d] |          #   more letters or
        [-'\d](?=[^\W_\d])  #   ', -, or digit followed by a letter
      )*
      [^\W_\d]              # and finishing with a letter
    )",
  RegexOptions.IgnorePatternWhitespace);

var input = "#@!@LOLOLOL YOU'VE BEEN *PWN3D* ! :') !!!1einszwei drei foo--bar!";

foreach (Match m in pattern.Matches(input))
  Console.WriteLine("[{0}]", m.Groups[1].Value);

生成以下输出

[LOLOLOL]
[YOU'VE]
[BEEN]
[PWN3D]
[einszwei]
[drei]
[foo]
[bar]

你能否正常地写一个正则表达式呢?我的意思是单行且没有额外的字符。 - Vyachaslav Gerchicov
好的解释。 - AnthonyVO

2
如果您只需要进行分词,无需使用正则表达式。首先,您可以通过删除所有非字母字符(保留空格)来对字符串进行清理处理,然后在空格字符上执行Split()操作。这将适用于大多数情况,虽然缩略语可能会有些棘手。至少这应该能帮助您入门。

好的,我想要做的是删除所有无效字符,但是如果 ' 和 - 字符不在字母字符之间,它们也是无效的。(在 "word-up" 中,- 是有效的,在 "word ----- up" 中,应该删除 - 字符...) - Led
你可以在其中放置一个检查,看看'-是否被字母字符包围,如果是,则不要删除。 - Jason

0
我写了一个类似于这样的字符串扩展:
    private static string[] GetWords(string text)
    {
        List<string> lstreturn = new List<string>();
        List<string> lst = text.Split(new[] { ' ' }).ToList();
        foreach (string str in lst)
        {
            if (str.Trim() == "")
            {
                lstreturn.Add(str);
            }
        }
        return lstreturn.ToArray();
    }

这个对我来说看起来不像是一个扩展。你是否缺少了一个 this - 41686d6564 stands w. Palestine

0

我的直觉是不使用正则表达式,而只是做一两个循环。

迭代字符串中的每个字符,如果不是有效字符,则用空格替换它,然后使用String.Split()并在空格上分割。

撇号和连字符可能会更难确定它们是垃圾字符还是合法字符。但是,如果您正在使用for循环迭代字符串,那么从当前字符向前和向后查看应该会有所帮助。

然后,您将拥有一个单词列表-对于这些单词中的每一个,请检查它们是否在您的字典中有效。如果您希望这样做快速,执行某种二进制搜索最好。但是,为了使其工作,线性搜索将更容易开始。

编辑:我之所以提到字典的事情,是因为我认为您可能只对合法单词感兴趣,即不是“asdfasdf”,但如果这不是您需要的,请忽略最后一句话。


你不想用空格替换无效字符。 - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接