JavaScript сопоставляет регулярное выражение с несколькими совпадениями в нужном месте - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть array токенов для сопоставления и регулярное выражение, которое получает начальную и конечную позиции каждого токена в пределах входного предложения.Это работает нормально, когда токен имеет одно вхождение.Когда токен встречается несколько раз, жадный Regex получит все совпадающие позиции токена в тексте, поэтому результирующая позиция для i-го вхождения токена будет отображаться последней найденной позицией.

Например, учитывая текст

var text = "Steve down walks warily down the street down\nWith the brim pulled way down low";

, первое вхождение токена down отображается на последнюю позицию в тексте, соответствующем RegExp, следовательно, у меня есть:

 {
    "index": 2,
    "word": "down",
    "characterOffsetBegin": 70,
    "characterOffsetEnd": 73
  }

Становится ясно, запустив этот пример:

var text = "Steve down walks warily down the street down\nWith the brim pulled way down low";
var tokens = text.split(/\s+/g);
var annotations = tokens.map((word, tokenIndex) => { // for each token
  let item = {
    "index": (tokenIndex + 1),
    "word": word
  }
  var wordRegex = RegExp("\\b(" + word + ")\\b", "g");
  var match = null;
  while ((match = wordRegex.exec(text)) !== null) {
    var wordStart = match.index;
    var wordEnd = wordStart + word.length - 1;
    item.characterOffsetBegin = wordStart;
    item.characterOffsetEnd = wordEnd;
  }
  return item;
});
console.log(annotations)

, где первое вхождение токена down должно быть первой совпадающей позицией:

 {
    "index": 2,
    "word": "down",
    "characterOffsetBegin": 6,
    "characterOffsetEnd": 9
  }

Так, учитывая, что я сопоставил позицию токеновдля каждого вхождения токена в тексте, т. е. первое вхождение down с первым совпадением, 2-е - со вторым совпадением и т. д. Я могу восстановить текст соответствующим образом с помощью charOffsetBegin и charOffsetEnd, следовательно, сделав так:

                var newtext = '';
                results.sentences.forEach(sentence => {
                    sentence.tokens.forEach(token => {
                        newtext += text.substring(token.characterOffsetBegin, token.characterOffsetEnd + 1) + ' ';
                    });
                    newtext += '\n';
                });

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

@ Ответ Феликса раскрывает причину вашей проблемы, но я бы хотел пойти дальше.

Я бы поместил все в класс (или конструктор), чтобы он содержался, и отделил логику извлечения совпадений из текста для каждого токена из итерации токенов.

class Annotations {
  constructor(text) {
    if(typeof text !== 'string') return null
    const opt = { enumerable: false, configurable: false, writeable: false }
    Object.defineProperty(this, 'text', { value: text, ...opt })
    Object.defineProperty(this, 'tokens', { value: text.split(/\s+/g), ...opt })
    for(let token of this.tokens) this[token] = Array.from(this.matchAll(token))
  }
  * matchAll(token) {
    if(typeof token === 'string' && this.text.indexOf(token) > -1) {
      const expression = new RegExp("\\b" + token + "\\b", "g")
      let match = expression.exec(this.text)

      while(match !== null) {
        const start = match.index
        const end = start + token.length - 1
        yield { start, end }
        match = expression.exec(this.text)
      }
    }
  }
}

const annotations = new Annotations("Steve down walks warily down the street down\nWith the brim pulled way down low")

console.log(annotations.text)
console.log(annotations.tokens)
console.log(annotations)
console.log(Array.from(annotations.matchAll('foo'))) // []
.as-console-wrapper { max-height: 100% !important }
0 голосов
/ 21 ноября 2018

Проблема не в том, что выражение является жадным, а в том, что вы ищете каждое совпадение токена во входной строке с вашим while циклом.

Вы должнысделать две вещи:

  • Прекратить итерации, как только вы нашли совпадение.
  • Отслеживайте предыдущие совпадения, чтобы вы могли их игнорировать.

Я считаю,это то, что вы хотите:

var text = "Steve down walks warily down the street down\nWith the brim pulled way down low";
var tokens = text.split(/\s+/g);
const seen = new Map();

var annotations = tokens.map((word, tokenIndex) => { // for each token
  let item = {
    "index": (tokenIndex + 1),
    "word": word
  }
  var wordRegex = RegExp("\\b(" + word + ")\\b", "g");
  var match = null;
  while ((match = wordRegex.exec(text)) !== null) {
    if (match.index > (seen.get(word) || -1)) {
      var wordStart = match.index;
      var wordEnd = wordStart + word.length - 1;
      item.characterOffsetBegin = wordStart;
      item.characterOffsetEnd = wordEnd;

      seen.set(word, wordEnd);
      break;
    }
  }
  return item;
});
console.log(annotations)

Карта seen отслеживает конечное положение самого последнего совпадения для токена.

Поскольку невозможно определитьДвижок регулярных выражений, чтобы игнорировать все до определенной позиции, мы все еще используем цикл while, но игнорируем любые совпадения, которые происходят до предыдущего матча, с if (match.index > (seen.get(word) || -1)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...