为了账号安全,请及时绑定邮箱和手机立即绑定

在js中用标记分割字符串

在js中用标记分割字符串

达令说 2023-08-05 19:30:45
我有一个带有一些标记的字符串:'This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}'我需要将其解析为一个数组,例如:[    {marker: false, value: 'This is'},    {marker: true,  value: 'the string'},    {marker: false, value: 'for'},    {marker: true, value:  'example.'}]因此保持句子顺序但添加标记信息。知道我怎样才能实现这一目标吗?谢谢
查看完整描述

3 回答

?
互换的青春

TA贡献1797条经验 获得超6个赞

这应该可以解决问题


const my_str = 'This is {startMarker} the string {endMarker} for {startMarker} example.{endMarker}';


const my_arr = my_str.split('{endMarker}').reduce((acc, s) =>

                  s.split('{startMarker}').map((a,i) =>

                      a && acc.push({

                        marker: i ? true : false,

                        value: a.trim()}))

                      && acc,[]);

     

console.log(my_arr)


查看完整回答
反对 回复 2023-08-05
?
繁花不似锦

TA贡献1851条经验 获得超4个赞

只是因为您是新贡献者......


interface MarkedString {

   marker: boolean

   value: string

}


function markString(text: string): MarkedString[] {

   let match: RegExpExecArray | null


   const firstMatch = text.slice(0, text.indexOf('{') - 1)

   

   const array: MarkedString[] = firstMatch.length > 0 ? [

      { marker: false, value: firstMatch  }

   ] : []

   

   while ((match = /\{(.+?)\}/g.exec(text)) !== null) {

      if (!match) break

   

      const marker = match[0].slice(1, match[0].slice(1).indexOf('}') + 1)

   

      const markerEnd = match.index + match[0].length

   

      const value = text.slice(markerEnd ,markerEnd + text.slice(markerEnd).indexOf('{')).trim()

   

      if (value === '') break

   

      if (marker === 'startMarker') {

         array.push({ marker: true, value })

      } else if (marker === 'endMarker') {

         array.push({ marker: false, value })

      }

   

      text = text.slice(markerEnd + value.length + 1)

   }

   

   return array

}


查看完整回答
反对 回复 2023-08-05
?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


const extract = (start, end, str) => Array.from(

  str.matchAll(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`),

  ([, text, mark]) => ({

    marker: mark === end,

    value: text.trim()

  })

);


console.log(extract(

  "{startMarker}",

  "{endMarker}",

  "This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}"

));

解释

正则表达式

我们发送以两个标记之一结尾的文本段。我们可以提取包括标记在内的每个部分。


This is {startMarker} the string {endMarker} 

^______^^___________^^__________^^_________^

| text       mark   ||   text        mark  |

^___________________^^_____________________^

       section               section

文本将成为value结果对象的文本,可以检查标记段是否是{endMarker}为了生成true或false用于结果对象。


因此,如果我们能够正确提取段和节,结果是:


result = {

  marker: marker === "{endMarker}",

  value: text.trim()

}

可以为我们执行此操作的正则表达式是:


/(.+?)(\{startMarker\}|\{endMarker\}|$)/g

请参阅 Regex101

  • (.+?)将匹配并捕获文本段

  • (\{startMarker\}|\{endMarker\}|$)将匹配并提取文本段末尾的标记。它还匹配行尾,以防最后一个标记后有更多文本,就像您有for {startMarker} example. {endMarker} more text here

一代

更一般地说,我们可以采用任何字符串作为开始和结束标记,然后对它们进行转义以确保它们字面匹配,即使其中存在像.或 之类的元字符*

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");

这样我们就可以将startand作为字符串并使用构造函数生成end正则表达式:RegExp

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


const start = "{startMarker}";

const end = "{endMarker}";


const regex = new RegExp(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`, "g");


console.log(regex.toString());

匹配

String#matchAll方法将生成一个迭代器,其中包含应用于字符串的正则表达式的所有匹配项。

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


const extract = (start, end, str) => {

  const sequence = str.matchAll(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`);

  

  for(const result of sequence) {

    console.log(result);

  }

};


extract(

  "{startMarker}",

  "{endMarker}",

  "This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}"

);

.matchAll()方法接受字符串作为参数,并使用RegExp构造函数自动将其转换为正则表达式,并进一步自动添加全局标志。然而,TypeScript 目前似乎不允许这样做 - 该方法的类型只允许一个RegExp对象,因此仅对于 TypeScript(直到类型修复)你必须调用

str.matchAll(new RegExp(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`, "g"))

转换为数组

将可迭代对象转换为数组的最简单方法是使用Array.from. 它采用的第一个参数可以是可迭代的,并且会自动转换为数组。第二个参数是在将每个元素放入数组之前应用的映射函数。

由于我们收到正则表达式匹配结果,我们可以使用此函数将它们直接转换为所需的项目:

result => {

  const match = result[1];

  const marker = result[2];


  return {

    marker: marker === end,

    value: match.trim()

  };

}

这给了我们更详细的版本:

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


const extract = (start, end, str) => {

  return Array.from(

    str.matchAll(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`),

    result => {

      const match = result[1];

      const marker = result[2];


      return {

        marker: marker === end,

        value: match.trim()

      };

    }

  );

}


console.log(extract(

  "{startMarker}",

  "{endMarker}",

  "This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}"

));

游乐场链接

然而,我们可以通过解构来减少所需的代码,它就变成了。


([, text, mark]) => ({

  marker: mark === end,

  value: text.trim()

})

这最终为我们提供了顶部的初始代码(再次包含在内,以避免向上滚动):

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


const extract = (start, end, str) => Array.from(

  str.matchAll(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`),

  ([, text, mark]) => ({

    marker: mark === end,

    value: text.trim()

  })

);


console.log(extract(

  "{startMarker}",

  "{endMarker}",

  "This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}"

));

关于 ES2020 兼容性的最后说明

String#matchAll来自 ES2020 规范。如果您当前没有瞄准该目标并且不想这样做,您可以使用工作方式非常相似的生成器函数轻松推出自己的版本:


function* matchAll(pattern, text) {

  const regex = typeof pattern === "string"

    ? new RegExp(pattern, "g")  //convert to global regex

    : new RegExp(pattern);      //or make a copy of the regex object to avoid mutating the input

    

  let result;

  while(result = regex.exec(text)) //apply `regex.exec` repeatedly

    yield result;                  //and produce each result from the iterator

}

这里唯一值得注意的遗漏是,String#matchAll如果传入非全局正则表达式对象,则会抛出错误。它仍然可以实现,但我使用了一个稍短的实现来进行说明。


使用自定义,matchAll您可以定位 ES2020 之前的版本,而无需填充

const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");


function* matchAll(pattern, text) {

  const regex = typeof pattern === "string"

    ? new RegExp(pattern, "g")

    : new RegExp(pattern);

    

  let result;

  while(result = regex.exec(text))

    yield result;

}


const extract = (start, end, str) => Array.from(

  matchAll(`(.+?)(${escapeRegex(start)}|${escapeRegex(end)}|$)`, str),

  ([, text, mark]) => ({

    marker: mark === end,

    value: text.trim()

  })

);


console.log(extract(

  "{startMarker}",

  "{endMarker}",

  "This is {startMarker} the string {endMarker} for {startMarker} example. {endMarker}"

));

查看完整回答
反对 回复 2023-08-05
  • 3 回答
  • 0 关注
  • 130 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信