regex 正则表达式分析多个单独的单词并忽略双引号

wj8zmpe1  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(80)

我正在尝试解析一个类似于下面的字符串。这表示对一本书的查询。有多个选项可用于查找特定字段,因此intitle:专门查找书名中的内容。我有两个问题。
1.它没有解析出第三个返回元素中的一些术语,比如inauthor和inpublisher - 'champ inauthor:“john smith”inpublisher:“the book place”-这可能与字符串中的双引号有关?
1.我如何使用双引号使它成为一个术语?
JSFiddle example
字符串:

basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156

我的尝试

let q: string = `basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156`;
console.log(q);
q = q.replaceAll(`: `, `:`);
console.log(q);
let all = q.split(
  /(\bintitle:\b|\binauthor:\b|\binpublisher:\b|\bsubject:\b|\bisbn:\b|\blccn:\b|\boclc:\b)/,
);
console.log(all);
[
  'basketball ',
  'intitle:',
  'champ inauthor:"john smith" inpublisher:"the book place" ',
  'subject:',
  'fiba ',
  'isbn:',
  '12345 ',
  'lccn:',
  '689778 ',
  'oclc:',
  '1234156'
]
uurv41yg

uurv41yg1#

正如注解中提到的,:\b将不匹配:",因为冒号后没有断字。
我建议使用matchAll并显式匹配引号中的部分。例如:

const q = `basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156`;

const matches = q.matchAll(/\s*(?:(\w+):\s*)?(?:"([^"]+)"|(\S+))/g);
const obj = Object.fromEntries(
    Array.from(matches, ([, key, val1, val2]) => [key ?? "__main", val1 ?? val2])
);
console.log(obj);
bq8i3lrv

bq8i3lrv2#

如果搜索词可以以不同的字符开始/结束,则可以在此处使用自适应词边界:

let q: string = `basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156`;
q = q.replaceAll(`: `, `:`);

let terms = ['intitle:', 'inauthor:', 'inpublisher:', 'subject:', 'isbn:', 'lccn:', 'oclc:'];
let regex: RegExp = new RegExp(String.raw`(?!\B\w)(${terms.map(x => x.replace(/[\/\-\\^$*+?.()|[\]{}]/g, '\\$&')).join('|')})(?!\B\w)`);

let all = q.split(regex);
console.log(all);

正则表达式看起来像

/(?!\B\w)(intitle:|inauthor:|inpublisher:|subject:|isbn:|lccn:|oclc:)(?!\B\w)/

其中(?!\B\w)只需要一个单词边界,如果在“word”的开始/结束的字符是单词字符。
如果有任何特殊的正则表达式元字符,terms.map(x => x.replace(/[\/\-\\^$*+?.()|[\]{}]/g, '\\$&')).join('|')部分将转义搜索terms

ukqbszuj

ukqbszuj3#

另一种不使用正则表达式的解决方案是首先在:上拆分字符串,然后在``上拆分每个部分,知道(除了最后一部分)每个部分由一个值和一个单词键组成。因此,我们在推送到输出数组之前,将键切掉并将值部分重新连接在一起:

let q = `basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156`;

let parts = q.split(/\s*:\s*/)

let all = parts.slice(0,-1).reduce((acc, s) => {
  const words = s.split(' ')
  const len = words.length
  return acc.concat([words.slice(0, -1).join(' '), words[len-1]])
}, []).concat(parts.slice(-1))

console.log(all)

注意,如果你想在键的末端使用:,只需在reduce中将words[len-1]更改为words[len-1] + ':'

ecfsfe2w

ecfsfe2w4#

你可以在RegExp中使用split来获取键和值的数组。
在第二步中,您可以清理键和值,并将它们放在map:Record<string,string>中。

let q = `basketball intitle:champ inauthor:"john smith" inpublisher:"the book place" subject: fiba isbn: 12345 lccn: 689778 oclc: 1234156`;

const arr = q.split(/\s+(\S+\s*:)\s*/g);
console.log(arr);

const map = {};
for (let i = 1; i < arr.length; i += 2) {
  const key = arr[i].replace(/\s*:/, "");
  const value = arr[i + 1];
  
  map[key] = (value.startsWith('"') && value.endsWith('"')) ? 
    value.slice(1, -1) : 
    value;
}

console.log(arr[0], map);
.as-console-wrapper{top:0;max-height:100%!important;}

相关问题