解析单个句子的正则表达式是什么?

26

我正在寻找一个好的.NET正则表达式,以便从一段文本中解析出单独的句子。

它应该能够将以下文本块解析为六个句子:

Hello world! How are you? I am fine.
This is a difficult sentence because I use I.D.

Newlines should also be accepted. Numbers should not cause  
sentence breaks, like 1.23.

这比我最初想象的要具有挑战性。

非常感谢任何帮助。我将使用这个来训练系统识别已知的文本体。


@Luke:看起来你想在示例文本中的“cause”和“sentence”之间有一个可见的换行符,但它没有显示出来。我通过在换行符前插入两个空格来强制显示它。这就是你想要的样子,对吧? - Alan Moore
是的,你说得对!感谢你修复了这个问题。我太傻了(还是个新手)。 - Luke Machowski
6个回答

42

尝试使用这个正则表达式@"(\S.+?[.!?])(?=\s+|$)"

string str=@"Hello world! How are you? I am fine. This is a difficult sentence because I use I.D.
Newlines should also be accepted. Numbers should not cause sentence breaks, like 1.23.";

Regex rx = new Regex(@"(\S.+?[.!?])(?=\s+|$)");
foreach (Match match in rx.Matches(str)) {
    int i = match.Index;
    Console.WriteLine(match.Value);
}

结果:

Hello world!
How are you?
I am fine.
This is a difficult sentence because I use I.D.
Newlines should also be accepted.
Numbers should not cause sentence breaks, like 1.23.

对于复杂的情况,当然需要像 SharpNLP 或 NLTK 这样的真正解析器。我的只是一个快速而简单的实现。

这里是SharpNLP信息和功能:

SharpNLP 是一组用 C# 编写的自然语言处理工具。目前提供以下 NLP 工具:

  • 句子分割器
  • 标记器
  • 块处理器(用于“查找非递归的语法注释,如名词短语块”)
  • 解析器
  • 名称查找工具
  • 指代工具
  • 与 WordNet 词汇数据库的接口

3
谢谢你指出SharpNLP,这是我之前没有见过的,可能非常有用。+1 - peter.murray.rust
最好使用前瞻断言来匹配 (?:\s+|$) - Gumbo
感谢您的信息,Gumbo。这样更好了,但我不得不在前面添加\S,因为空格必须在左侧剥离。 - YOU
谢谢大家,这些见解非常有用。我会在接下来的几天尝试一下。 - Luke Machowski

5
var str = @"Hello world! How are you? I am fine. This is a difficult sentence because I use I.D.
Newlines should also be accepted. Numbers should not cause sentence breaks, like 1.23.";

Regex.Split(str, @"(?<=[.?!])\s+").Dump();

我在LINQPad中进行了测试。


5
无法使用正则表达式来解析自然语言。什么是句子的结尾?句号可以出现在许多地方(例如,e.g.)。您应该使用自然语言处理工具包,例如OpenNLP或NLTK。不幸的是,在C#中几乎没有提供任何此类工具包。因此,您可能需要创建Web服务或以其他方式链接到C#。
请注意,如果您依赖精确的空格,例如“I.D.”,这将在将来引起问题。很快你会发现一些打破你的正则表达式的例子。例如,大多数人在他们的缩写后面放置空格。
WP中有一个关于开放和商业提供的优秀概述(http://en.wikipedia.org/wiki/Natural_language_processing_toolkits)。我们已经使用了其中的几个。值得努力。
[您使用“train”一词。这通常与机器学习(这是NLP的一种方法,并已用于句子分割)相关联。实际上,我提到的工具包包括机器学习。我怀疑那不是你的意思——而是你会通过启发式演化你的表达式。不要这样做!]

谢谢提供这些信息。我一直对机器学习方面很感兴趣,这是我想要探究的一个方面。对于我的当前目的,我认为简单的正则表达式方法(在我不期望出现你所说的奇怪情况时)已经足够了。然而,我会尝试你提到的框架,因为它们已经存在。 - Luke Machowski

2

仅使用正则表达式实现这个功能是不可能的,除非您确切地知道哪些“困难”的标记,例如“i.d.”,“Mr.”等。例如,“请出示您的身份证明,邦德先生。”有多少句话?我不熟悉任何C#实现,但我已经使用过NLTK的Punkt tokenizer。重新实现可能不会太难。


0

大多数人建议使用SharpNLP,除非你想让你的QA部门遇到错误。

但是,由于你可能面临某种压力。这里有另一种处理“Dr.”和“X.”等单词的尝试。但是,它将无法处理以“it。”结尾的句子。

你好,世界!你怎么样?我很好。这是一个困难的句子,因为我使用了I.D. 换行符也应该被接受。数字不应该导致句子中断,比如1.23。请参阅Dr. B或Mr. FooBar,以评估贲门处的幽门螺杆菌。

    var result = new Regex(@"(\S.+?[.!?])(?=\s+|$)(?<!\s([A-Z]|[a-z]){1,3}.)").Split(input).Where(s => !String.IsNullOrWhiteSpace(s)).ToArray<string>();
    foreach (var match in result) 
    {
        Console.WriteLine(match);
    }

0

我使用了这里发布的建议,并得出了似乎可以实现我想要做的事情的正则表达式:

(?<Sentence>\S.+?(?<Terminator>[.!?]|\Z))(?=\s+|\Z)

我使用了Expresso来生成:

//  using System.Text.RegularExpressions;
/// <summary>
///  Regular expression built for C# on: Sun, Dec 27, 2009, 03:05:24 PM
///  Using Expresso Version: 3.0.3276, http://www.ultrapico.com
///  
///  A description of the regular expression:
///  
///  [Sentence]: A named capture group. [\S.+?(?<Terminator>[.!?]|\Z)]
///      \S.+?(?<Terminator>[.!?]|\Z)
///          Anything other than whitespace
///          Any character, one or more repetitions, as few as possible
///          [Terminator]: A named capture group. [[.!?]|\Z]
///              Select from 2 alternatives
///                  Any character in this class: [.!?]
///                  End of string or before new line at end of string
///  Match a suffix but exclude it from the capture. [\s+|\Z]
///      Select from 2 alternatives
///          Whitespace, one or more repetitions
///          End of string or before new line at end of string
///  
///
/// </summary>
public static Regex regex = new Regex(
      "(?<Sentence>\\S.+?(?<Terminator>[.!?]|\\Z))(?=\\s+|\\Z)",
    RegexOptions.CultureInvariant
    | RegexOptions.IgnorePatternWhitespace
    | RegexOptions.Compiled
    );


// This is the replacement string
public static string regexReplace = 
      "$& [${Day}-${Month}-${Year}]";


//// Replace the matched text in the InputText using the replacement pattern
// string result = regex.Replace(InputText,regexReplace);

//// Split the InputText wherever the regex matches
// string[] results = regex.Split(InputText);

//// Capture the first Match, if any, in the InputText
// Match m = regex.Match(InputText);

//// Capture all Matches in the InputText
// MatchCollection ms = regex.Matches(InputText);

//// Test to see if there is a match in the InputText
// bool IsMatch = regex.IsMatch(InputText);

//// Get the names of all the named and numbered capture groups
// string[] GroupNames = regex.GetGroupNames();

//// Get the numbers of all the named and numbered capture groups
// int[] GroupNumbers = regex.GetGroupNumbers();

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