在JavaScript中将字符串拆分为句子

28

目前我正在开发一个应用程序,它可以将一长列拆分为多个短列。为此,我将整个文本拆分为单词,但是目前我的正则表达式也将数字拆分。

我所做的是这样的:

str = "This is a long string with some numbers [125.000,55 and 140.000] and an end. This is another sentence.";
sentences = str.replace(/\.+/g,'.|').replace(/\?/g,'?|').replace(/\!/g,'!|').split("|");

结果是:

Array [
    "This is a long string with some numbers [125.",
    "000,55 and 140.",
    "000] and an end.",
    " This is another sentence."
]

期望的结果应该是:

Array [
    "This is a long string with some numbers [125.000, 140.000] and an end.",
    "This is another sentence"
]

我需要如何修改我的正则表达式才能实现这个目标?我需要注意一些可能会遇到的问题吗?或者只需搜索". ""? ""! "就足够了吗?


你能改变这个字符串吗?还是这不是一个选项? - Beejee
你是在寻找一个可行的正则表达式来得到所需的结果,还是已经知道了并且想要关于其他潜在问题的建议? - Harry
@Beejee:我可以操作这个字符串。 - Tobias Golbs
“或者只搜索“. ”,”? “和”! “是否足够好呢?” - 不行,因为它不允许在缩写中使用“. ”:“我们应该去联邦调查局还是语法警察?” - nnnnnn
8个回答

44
str.replace(/([.?!])\s*(?=[A-Z])/g, "$1|").split("|")

输出:

[ 'This is a long string with some numbers [125.000,55 and 140.000] and an end.',
  'This is another sentence.' ]

分解:

([.?!]) = 匹配., ?, 或 !中的任意一个。

\s* = 匹配前面的标记 ([.?!]) 后面的 0 个或多个空格字符。这考虑到符合英语语法的标点符号后面可能有空格。

(?=[A-Z]) = 前面的标记只有当下一个字符在 A-Z 的范围内(大写字母 A 到大写字母 Z)时才匹配成功。大多数英语语言的句子都以大写字母开头。之前的正则表达式没有考虑到这一点。


替换操作使用:

"$1|"
我们使用了一个“捕获组”([.?!]),捕获其中一种字符,并将其替换为$1(匹配项)加上|。因此,如果我们捕获了?,则替换结果将是?|
最后,我们拆分了|管道并得到了我们的结果。
因此,本质上,我们的意思是:
1)查找标点符号(.?!之一),并捕获它们
2)标点符号可以选择在其后包括空格。
3)标点符号后面,我期望有一个大写字母。
与先前提供的正则表达式不同,这会正确匹配英语语法。
从那里开始:
4)我们通过附加管道|来替换捕获的标点符号
5)我们拆分管道以创建一个句子数组。

“我的女儿现在10岁。再过10年,她就20岁了。” - JSPP
抱歉,我本意是想将条件A中的“numbers”改为“digits”,但显然我不能在5分钟后进行编辑。小数点后面的数字不能在它们之前有空格,就像英语语法中一样。如果允许这样做,那么就会变得模棱两可,就像我提供的例句一样。你提出的条件已经在我的修订正则表达式中考虑并满足了,不是吗? - JSPP
修改后的正则表达式假定存在空格。因此,这个注释将无法正确分割。 - Tibos
是的,这是在修订后的正则表达式中包含的一个条件。 - JSPP
Tibos,你上一条有关间距的评论直接与你之前关于歧义的言论相矛盾和否定。你在抓住稻草。 - JSPP
显示剩余8条评论

15
str.replace(/(\.+|\:|\!|\?)(\"*|\'*|\)*|}*|]*)(\s|\n|\r|\r\n)/gm, "$1$2|").split("|")

RegExp(详见Debuggex):

  • (.+|:|!|\?) = 句子不仅可以以 "."、"!" 或 "?" 结尾,还可以以 "..." 或 ":" 结尾
  • (\"|\'|)*|}|]) = 句子可以被引号或括号包围
  • (\s|\n|\r|\r\n) = 句子后必须是空格或行末
  • g = 全局匹配
  • m = 多行匹配

注意:

  • 如果使用 (?=[A-Z]),则该 RegExp 在某些语言中无法正确工作。例如,"Ü"、"Č" 或 "Á" 将无法被识别。

7
你可以利用下一句话以大写字母或数字开头的特点进行攻击。
.*?(?:\.|!|\?)(?:(?= [A-Z0-9])|$)

正则表达式可视化

Debuggex演示

它将此文本分割

This is a long string with some numbers [125.000,55 and 140.000] and an end. This is another sentence. Sencenes beginning with numbers work. 10 people like that.

将段落转换为句子:

This is a long string with some numbers [125.000,55 and 140.000] and an end.
This is another sentence.
Sencenes beginning with numbers work.
10 people like that.

jsfiddle


这很棒,我刚注意到它不能处理不良用户输入,例如“Jim went to the store. Larry slept until 12. But Becky left for the weekend.” 但这已超出问题的范围。我只是提一下给像我这样寻找快速正则表达式处理方法的人。 - Quinxy von Besiex
这也无法处理?或! - AWhatley

5

使用先行断言来避免在点号后面没有空格和单词字符时进行替换:

sentences = str.replace(/(?=\s*\w)\./g,'.|').replace(/\?/g,'?|').replace(/\!/g,'!|').split("|");

输出:

["This is a long string with some numbers [125.000,55 and 140.000] and an end. This is another sentence."]

4

使用前瞻来确保点号后面不是数字,这样更安全。

var str ="This is a long string with some numbers [125.000,55 and 140.000] and an end. This is another sentence."

var sentences = str.replace(/\.(?!\d)/g,'.|');
console.log(sentences);

如果您想更安全,可以检查后面是否也是数字,但由于JS不支持后顾,您需要捕获前一个字符并在替换字符串中使用它。
var str ="This is another sentence.1 is a good number"

var sentences = str.replace(/\.(?!\d)|([^\d])\.(?=\d)/g,'$1.|');
console.log(sentences);

一种更简单的解决方案是在数字内部转义点号(例如用$$$$替换它们),进行分割后再取消转义点号。


这是唯一一个对我完美运作的版本(第一个版本)。 - Milad.Nozari

3
你在正则表达式中忘记加上 '\s' 了。
试试这个:
var str = "This is a long string with some numbers [125.000,55 and 140.000] and an end. This is another sentence.";
var sentences = str.replace(/\.\s+/g,'.|').replace(/\?\s/g,'?|').replace(/\!\s/g,'!|').split("|");
console.log(sentences[0]);
console.log(sentences[1]);

http://jsfiddle.net/hrRrW/


3

我只需更改字符串并在每个句子之间添加一些内容。

你告诉我你有权更改它们,这样做会更容易。

\r\n

通过这样做,您将拥有一个要搜索的字符串,并且您不需要使用这些复杂的正则表达式。

如果您想以更困难的方式完成,我会使用正则表达式查找 "." "?" "!" 后跟大写字母。就像Tessi向您展示的那样。


0

回答@Roger Poon和@Antonín Slejška的方法效果不错。

如果我们添加trim函数和过滤空字符串会更好:

const splitBySentence = (str) => {
  return str.replace(/([.?!])(\s)*(?=[A-Z])/g, "$1|")
    .split("|")
    .filter(sentence => !!sentence)
    .map(sentence => sentence.trim());
}

const splitBySentence = (str) => {
  return str.replace(/([.?!])(\s)*(?=[A-Z])/g, "$1|").split("|").filter(sentence => !!sentence).map(sentence => sentence.trim());
}

const content = `
The Times has identified the following reporting anomalies or methodology changes in the data for New York:

May 6: New York State added many deaths from unspecified days after reconciling data from nursing homes and other care facilities.

June 30: New York City released deaths from earlier periods but did not specify when they were from.

Aug. 6: Our database changed to record deaths by New York City residents instead of deaths that took place in New York City.

Aug. 20: New York City removed four previously reported deaths after reviewing records. The state reported four new deaths in other counties.(extracted from NY Times)
`;

console.log(splitBySentence(content));


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