【问题标题】:Splitting a string into words and keeping delimiter将字符串拆分为单词并保留分隔符
【发布时间】:2014-11-28 20:03:41
【问题描述】:

我想将一个字符串(句子)拆分成一个单词数组并保留分隔符。


我已经找到并且我目前正在为此使用这个正则表达式:

[^.!?\s][^.!?]*(?:[.!?](?!['"]?\s|$)[^.!?]*)*[.!?]?['"]?(?=\s|$)

可以在这里找到解释:http://regex101.com/

这完全按照我的意愿工作,并且有效地制作了一个类似

的字符串

This is a sentence.

到一个数组

["This", "is", "a", "sentence."]

这里的问题是它包含空格或换行符。我希望字符串被解析为单词,但我也希望 对应的空格和/或换行符属于前一个单词

我已经阅读了正向前瞻,它应该寻找未来的字符(空格和/或换行符),但在提取单词时仍将它们考虑在内。虽然这可能是我未能实施的解决方案。

如果有什么不同,我正在使用 JavaScript 和以下代码:

//save the regex -- g modifier to get all matches
var reg =  /[^.!?\s][^.!?]*(?:[.!?](?!['"]?\s|$)[^.!?]*)*[.!?]?['"]?(?=\s|$)/g;

//define variable for holding matches
var matches;
//loop through each match
while(matches = reg.exec(STRING_HERE)){
    //the word without spaces or newlines
    console.log(matches[0]);
}

代码有效,但正如我所说,它包含空格和换行符

【问题讨论】:

  • 提供一个例子来复制这个The problem here is that it does not include spaces nor newlines.问题。
  • 抛弃丑陋的正则表达式,使用拆分,并手动添加换行符:"This is a sentence".split(" ").map(function(word){ return word+" "; })
  • @PSkocik - 这也会在最后一个单词之后添加空格,这原本不存在。
  • @Chris 从最后一个中删除它并不难。我的观点是,我认为这不是正则表达式的好用处。
  • 我同意@PSkocik。这正是我在写答案时的想法。有时简单的工具就能完成工作。

标签: javascript regex


【解决方案1】:

如果您想在单词后包含空格,正则表达式 \S+\s* 应该可以工作。

const s = `This is a sentence.
This is another sentence.`;

console.log(s.match(/\S+\s*/g))

【讨论】:

    【解决方案2】:

    一般解决方案

    为了使分隔符在结果中保持连接,正则表达式必须是零宽度匹配。换句话说,正则表达式可以被认为是匹配分隔符和非分隔符之间的点,而不是匹配分隔符本身。这可以通过零宽度匹配表达式来实现,在分割点之前、处或之后进行匹配(每个最多一个);我们称之为ABC。有时一个子表达式就可以了,其他的你需要两个;顺便说一句,我想不出你需要三个的情况。

    不仅前瞻,而且lookarounds 通常都是此目的的完美候选者:后视 ((?<=...)) 匹配分割点之前,前瞻 ((?=...)) 之后匹配。这就是这种方法的精髓。可以使用正面或负面的环视。一个陷阱是,对于 JS 正则表达式来说,lookbehinds 相对较新,因此并非所有browsers 或其他 JS 引擎都支持它们(当前版本的 Firefox、Chrome、Opera、Edge 和 node.js 支持;Safari 不支持)。如果您需要支持不支持后视的 JS 引擎,您仍然可以编写和使用匹配 at-and-before (BC) 的正则表达式。

    要让分隔符出现在每个匹配项的末尾,请将它们放入A。让他们在一开始,在C。幸运的是,JS 正则表达式不会放置restrictions on lookbehinds,因此只需将定界符正则表达式包装在正环视标记中就可以满足定界符的全部要求。如果分隔符不是那么简单(即上下文相关),编写正则表达式可能需要更多的工作,它不需要匹配整个分隔符。

    与分隔符模式配对,您需要编写一个匹配非分隔符的开始(C)或结束(A)的模式。这一步可能需要最多的额外工作。

    分割点匹配,B 通常(总是?)是一个简单的boundary,例如\b

    具体解决方案

    如果空格是唯一的分隔符,并且它们出现在每个匹配项的末尾,则分隔符模式将是(?<=\s),在A 中。但是,问题描述中未涵盖某些情况。例如,是否应该拆分仅由标点符号(例如“x.y”)分隔的单词?引号和连字符应该出现在分割点的哪一侧(如果有)?它们应该算作标点符号吗?分隔符的另一个选项是匹配(之后)所有非单词字符,在这种情况下,A 将是 (<?=\W)

    由于分割点位于单词边界,B 可能是\b

    由于匹配的开头是一个单词字符,(?=\w) 就足够了 C

    这三个中的任何两个就足够了。 /(<?=\W)(?=\w)/ 的意思可能最清楚(并且在最多点处分裂),它可以翻译为“在每个单词的开头分裂”。 \b 可以添加,如果您觉得它更容易理解,尽管它没有功能影响:/(<?=\W)\b(?=\w)/

    请注意,Oriol 的出色解决方案由B=\b 和(C=(?!\s)C=(?![\s.]))给出。

    附加

    作为一个兴趣点,如果 JS 正则表达式支持 TCL word boundaries\m 仅在单词的开头匹配,那么对于这种特殊情况会有一个更简单的解决方案,因此 str.split(/\m/) 将在开头完全拆分每个字。 (\m 等价于(<?=\W)(?=\w)。)

    【讨论】:

      【解决方案3】:

      你可以试试更简单的:

      str.split(/\b(?!\s)/);
      

      但是,请注意非单词字符(例如句号)将被视为另一个单词:

      "This is a sentence.".split(/\b(?!\s)/);
      // [ "This ", "is ", "a ", "sentence", "." ]
      

      要解决这个问题,您可以使用一个字符类,其中包含不应以另一个单词开头的字符:

      str.split(/\b(?![\s.])/);
      

      【讨论】:

      • 就像 PSocik 提供的答案一样,我所做的第一次测试证明了这段代码确实适用于我想要实现的目标,如果是这样,稍后会回来接受作为答案。
      【解决方案4】:
      function split_string(str){    
         var arr = str.split(" ");
         var last_i = arr.length - 1;
         for(var i=0; i<last_i; i++){
             arr[i]+=" ";
         }
         return arr;
       }
      

      【讨论】:

      • 正如帖子中的 cmets 所指出的,我可能以错误的方式看待这项任务,这使我寻找一种过于复杂的方法。我所做的第一个测试证明这段代码确实适用于我想要实现的目标,如果是这样,稍后会回来接受作为答案。
      • 经过进一步测试,我可以看到它确实有效,但是对于所有包含换行符的单词,它结合了第一个单词、换行符和下一个单词。示例 jsfiddle:jsfiddle.net/r71msxrh/4
      【解决方案5】:

      可能就这么简单:

      var sentence = 'This is a sentence.';
      sentence = sentence.split(' ').join(' ||');
      sentence = sentence.split('\n').join('\n||');
      var matches = sentence.split('||');
      

      请注意,我使用 2 个竖线作为分隔符,但当然你可以使用任何东西,只要它是唯一的。

      另外请注意,我只将 \n 拆分为换行符,但您也可以添加 \r\n 或您想要拆分的任何内容。

      【讨论】:

        猜你喜欢
        • 2017-04-04
        • 1970-01-01
        • 2016-11-26
        • 2022-11-03
        • 1970-01-01
        • 2018-12-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多