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)
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
}
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
(.+?)
将匹配并捕获文本段(\{startMarker\}|\{endMarker\}|$)
将匹配并提取文本段末尾的标记。它还匹配行尾,以防最后一个标记后有更多文本,就像您有for {startMarker} example. {endMarker} more text here
一代
更一般地说,我们可以采用任何字符串作为开始和结束标记,然后对它们进行转义以确保它们字面匹配,即使其中存在像.
或 之类的元字符*
。
const escapeRegex = s => s.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
这样我们就可以将start
and作为字符串并使用构造函数生成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}"
));
添加回答
举报