перенос строки слова javascript на совпадения в другой строке - PullRequest
4 голосов
/ 15 июня 2011

Если у меня есть определенная строка ввода, которую я хочу сравнить с другой строкой и сопоставить совпадения входной строки с другой строкой, используя максимально возможное совпадение.Как бы я лучше обернуть спички в теге?Это нетривиальный вопрос.

По сути, я хочу сопоставить введенную строку с другой строкой, используя тег span, чтобы показать совпавшие части цели, найденной во введенной строке.

  • Совпадение с начала входной строки первым (максимально возможное совпадение)
  • Необходимо выделить частичное совпадение поискового слова (см., Например, «баржа», «баржи»)
  • Специальный символразрывы должны совпадать с "fred / dred". Введенные слова будут состоять из двух слов.
  • Строка ввода будет меняться в зависимости от того, что вводят пользователи.
  • Соответствует строке ввода с самого начала в качестве приоритета
  • Соответствует каждому слову, где оно встречается

если пользователь вводит строку из нескольких слов, я хочу, чтобы их совпадения начинались постепенно, начинаяс самого начала, где они встречаются во второй строке.Они могут иметь или не иметь пробелов в начале / конце вводимой строки.Я хотел бы, чтобы наибольшая часть была обернута.

Пример ввода строк:

"Brown cats cannot be white cats"
"blue pigs "
"large, charged/marged barge pigs"

Я бы хотел, чтобы они были обернуты так:

"<span class='wrapper'>Brown cats cannot be white cats</span>"

в строке назначениягде встречаются совпадения, даже частичные, но с максимально возможным совпадением.

Пример строки для переноса:

"Hi bill, brown cats cannot be white cats and cows are not blue pigs, blue melons are large but not batteries charged barges with white cats carry coal"

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

"Hi bill, <span class='wrapper'>brown cats cannot be white cats</span> and cows are not blue pigs, blue melons are large but not batteries charged barges with <span class='wrapper'>white cats</span> carry coal"

"Hi bill, brown cats cannot be white cats and cows are not <span class='wrapper'>blue pigs</span>, blue melons are large but not batteries charged barges with white cats carry coal"

"Hi bill, brown cats cannot be white cats and cows are not blue <span class='wrapper'>pigs</span>, blue melons are large but not batteries <span class='wrapper'>charged</span> <span class='wrapper'>barge</span>s with white cats carry coal"

Возможные совпадения для: «Коричневые кошки не могут быть белыми кошками»

"Brown cats cannot be white cats"
"Brown cats cannot be white"
"Brown cats cannot be"
"Brown cats cannot"
"Brown cats"
"Brown"
"Brown" "cats" "cannot" "be" "white" "cats"

Если бы мне нужно было просто обернуть каждое соответствующее слово, я мог бы сделать:

function replaceWords(wordsy, text) {
   var re = '(' + wordsy + ')(?![^<]*(?:<\/script|>))',
       regExp = new RegExp(re, 'ig'),
       sTag = "<span class='wrapper'>",
       eTag = "</span>";
   return text.replace(regExp, sTag + '$&' + eTag);
};
var matchstring = "Brown cats cannot be white cats";
var wrapstring = "Hi bill, brown cats cannot be white cats and cows are not blue pigs, blue melons are large but not batteries charged barges with white cats carry coal";
var words = myValue.split(" ");
var i = words.length; while (i--) {
    wrapstring = replaceWords(words[i], wrapstring );
};

Это не удовлетворяеттребование для "наибольшего совпадения".Я хотел бы получить максимально возможное совпадение из любой части строки совпадений, которая встречается в строке wrapstring.

Решения, использующие чистый javascript или jquery или комбинацию, являются приемлемыми.

EDIT: Некоторые предложили KMP, вот пример KMP jsfiddle.net / y5yJY / 2 , но это не так, его текущая форма соответствует всем критериям и выполняет одно совпадение.

Ответы [ 3 ]

2 голосов
/ 21 июня 2011

У меня есть интересное решение, которое должно работать как ваша оригинальная спецификация.Он не был подвергнут стресс-тестированию, я не уверен, хотите ли вы обрабатывать большой объем текста или нет, и он делает довольно много совпадений с регулярными выражениями.Не обязательно самое чистое или простое решение, но оно работает по назначению.

Особенности и ограничения:

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

  • В настоящее время вы не можете надежно иметь символы [ и ] в исходной строке, так какони используются внутри.Если это проблема, вы должны поменять их на любой другой символ или комбинацию символов перед сопоставлением.

  • Для строки совпадения N слов замена 2*N + 5 строки выполняется с использованиемрегулярные выражения различной сложности.

  • Соответствует словам и фразам без учета регистра, игнорируя любые несловесные символы.В то же время, он сохраняет слова со смешанным регистром и несловарные символы в результате.

Как это работает:

  1. Сначала он ищет каждое слово отдельно и добавляет к ним их индекс в строке соответствия в квадратных скобках: word[2].Если слово появляется несколько раз, к нему добавляются все индексы: word[3][2][1].

  2. Далее, он находит и помечает слова, которые не находятся в границах переноса, просматривая индексы окружающих слов,На отдельном шаге он удаляет индексы из этих слов.В конце концов one[1] two[2] three[3] станет one[1] []two three[3].

  3. Теперь осталось только сделать некоторые предположения в определенном порядке и обернуть слова / фразы.Взгляните на код, чтобы увидеть все выполненные замены.

Важно, что после первого шага мы никогда не сопоставляем слова напрямую, с этого момента слово просто упоминаетсякак any number of word characters before [index] или any number of word characters after [].Это гарантирует, что мы правильно упаковываем повторяющиеся слова / фразы.

Посмотрите на это демо .Я добавил эффект наведения, чтобы вы могли видеть, какие слова сгруппированы и обернуты вместе.

А вот сумасшедший код, наслаждайтесь!

var matchstring = 'Brown cats cannot be white cats';
var wrapstring = 'Hi bill, brown cats cannot be white cats and cows are not blue pigs, blue melons are large but not batteries charged barges with white cats carry coal, and the word "cannot" should match ';

// Pre-process matchstring to make it a flat list of words
// separated by single spaces.
matchstring = matchstring.replace(/\W+/g,' ');

var wrapStart = '<span class="wrapped">';
var wrapEnd = '</span>';

var matcharray = matchstring.split(' ');
var i, reg;

// Mark all matched words with indices
// one -> one[1]
for (i = 0; i < matcharray.length; i++) {
    reg = new RegExp('\\b' + matcharray[i] + '\\b', 'ig');
    wrapstring = wrapstring.replace(reg, '$&[' + i + ']');
}

// Mark all inner words
// one[1] two[2] three[3] -> one[1] []two[2] three[3]
for (i = 1; i < matcharray.length; i++) {
    reg = new RegExp('\\b(\\w+)([\\]\\d\\[]*\\[' + (i - 1) + '\\][\\]\\d\\[]*)(\\W+)(\\w+)([\\]\\d\\[]*\\[' + i + '\\][\\]\\d\\[]*)(?=\\W+\\w+[\\[\\d\\]]*\\[' + (i + 1) + '\\])', 'ig');
    wrapstring = wrapstring.replace(reg, '$1$2$3[]$4$5');
}

// Remove indices from inner words
// one[1] []two[2] three[3] -> one[1] []two three[3]
wrapstring = wrapstring.replace(/\[\](\w+)[\[\d\]]*/g, '[]$1');

// Start tags
// one[1] []two three[3] -> {one []two three[3]
wrapstring = wrapstring.replace(/(\w+)\[[\[\d\]]+\](\W+)\[\]/g, wrapStart + '$1$2[]');

// End tags
// {one []two three[3] -> {one []two three}
wrapstring = wrapstring.replace(/\[\](\w+\W+\w+)\[[\[\d\]]+\]/g, '$1' + wrapEnd);

// Wrap double words
// one[1] two[2] -> {one two}
wrapstring = wrapstring.replace(/(\w+)\[[\[\d\]]+\](\W+\w+)\[[\[\d\]]*\]/g, wrapStart + '$1$2' + wrapEnd);

// Orphan words
// unmatched matched[1] unmatched -> unmatched {matched} unmatched
wrapstring = wrapstring.replace(/(\w+)\[[\[\d\]]+\]/g, wrapStart + '$1' + wrapEnd);

// Remove left-over tags
// []word -> word
wrapstring = wrapstring.replace(/\[\]/g, '');

alert(wrapstring);

Соответствие частичным словам

Как уже упоминалось, после первого шага слова обрабатываются только по их добавленным индексам.Это означает, что если мы хотим сделать какое-то умное сопоставление вместо целых слов, нам просто нужно изменить регулярное выражение в первом цикле for.Это фрагмент кода, с которым мы будем играть в этом разделе:

reg = new RegExp('\\b' + matcharray[i] + '\\b', 'ig');

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} 10 * в регулярном выражении означает соответствие границ слова, то есть начало или конец последовательности символов слова.Вот почему вышеприведенное \bword\b регулярное выражение дает нам только целые слова, так как word должен быть окружен границами слов.Но это не обязательно должно быть так.

Если мы хотим сопоставить все слова в тексте, начинающемся с нашего ключевого слова, мы можем изменить приведенную выше строку следующим образом:

reg = new RegExp('\\b' + matcharray[i] + '\\w*\\b', 'ig');

Это приводит к регулярному выражению \bword\w*\b.Он соответствует всем word последовательностям символов, за которыми следуют 0 или более дополнительных символов слова (\w*), окруженных границами слова.Обратите внимание, что обратные слэши необходимо экранировать в строках javascript (\\ означает один \).

В зависимости от требований мы можем легко создавать дополнительные комбинации регулярных выражений:

  • \bword\w*\b соответствует словам, начинающимся с ключевого слова.
  • \b\w*word\b соответствует словам, заканчивающимся ключевым словом.
  • \b\w*word\w*\b соответствует словам, содержащим ключевое слово.
  • \b(\w*word|word\w*)\b соответствуетслова, оканчивающиеся или начинающиеся с ключевого слова.

Вы даже можете сказать, что хотите сопоставить только незначительные модификации слов.Например, \b\w{0,2}word\w{0,2}\b будет соответствовать слову, только если оно имеет максимум двухбуквенный префикс и / или суффикс.Таким образом, danger будет соответствовать endanger, cat будет соответствовать cats, но can не будет соответствовать cannot, так как это будет 3 дополнительные буквы.

Сопоставить сложные формы множественного числа и неправильные глаголы непросто, вы можете создать огромный словарь неправильных слов на вашем сервере и предварительно обработать слово, поэтому, если пользователь вводит foot, с помощью регулярного выражения \b(foot|feet)\b будет соответствоватьобе формы.Более простым решением будет заботиться только о регулярных словах.Для большинства слов совпадения \bword(s|es|)\b будет достаточно, чтобы поймать множественное число, оно также соответствует word, words и wordes.Для таких слов, как fly, регулярное выражение \bfl(y|ies)\b выполнит эту работу.Для таких слов, как index, регулярное выражение \bind(ex|exes|ices)\b будет соответствовать большинству распространенных форм.

Поскольку я не очень разбираюсь в языке, я пока просто оставлю это здесь.

Подстановочные знаки во входных данных

Как и выше, очень легко добавить поддержку подстановочных знаков во входной строке.Допустим, мы хотим, чтобы пользователь ввел ?, что означает любой символ .Если введено значение ?red, нам просто нужно заменить ? на \w в нашем регулярном выражении.Например, \b\wred\b будет соответствовать fred и dred.

Как и выше, вы также можете использовать несколько символов подстановки, заменив их на \w+ для одного или нескольких символов или \w* для ноль или более символов .\bf\w+d\b будет соответствовать fed и feed, \w* будет соответствовать fd.

1 голос
/ 20 июня 2011

Вот что я сделал для решения этой проблемы: (ищу улучшения, поскольку оно не идеально) (это завернуто в готовый документ jQuery) как здесь: http://jsfiddle.net/KvM47/

function findStringLimit(searchChar, searchCharIndex, searchedString) {
    return searchedString.substring(0, searchedString.lastIndexOf(searchChar, searchCharIndex));
};

function replaceWords(wordsy, text) {
    var re = '(' + wordsy + ')(?![^<]*(?:<\/script|>))',
        regExp = new RegExp(re, 'ig'),
        sTag = "<span class='wrappedWord'>",
        eTag = "</span>";
    return text.replace(regExp, sTag + '$&' + eTag);

};
var longstring = $('#mystring');
var htmlString =longstring .html(); //  instance html
myValue = "Brown cats cannot be white cats";
myValue = myValue.replace(/^\s+|\s+$/g, "");//trim whitespace at each end

var words = myValue.split(" ");
var allPhrases = [];
allPhrases.push(myValue);

var i = words.length;
while (i--) {
    allPhrases.push(findStringLimit(" ", allPhrases[(words.length - i) - 1].length, allPhrases[(words.length - i) - 1]));
};

var i = allPhrases.length;
while (i--) {
    if (allPhrases[i] != "") words = words.concat(allPhrases[i]);
};
var i = words.length;
while (i--) {
    htmlString = replaceWords(words[i], htmlString);
};
longstring.html(htmlString);

Что нужно улучшить:

  • используйте другие символы для разделения слов, а не только пробелы.
  • сделать его более эффективным
  • лучшее обнаружение «кусков» строк (двух или более слов вместе) в строках «поиск» и «сопоставление» и их обработка.
1 голос
/ 17 июня 2011

Что насчёт этого: (описывает только алгоритм, не записан в коде)

Представьте, что у вас есть две строки, написанные на двух листах бумаги. Разместите два листа так, чтобы один был над другим. Переместите верхний лист влево, чтобы его последняя буква была сверху первой буквы нижнего листа. Теперь совпадают ли две перекрывающиеся буквы? Если это так, у вас есть совпадение длины 1. Запишите это как самое длинное совпадение. Затем переместите верхний лист на один символ вправо. Теперь две буквы перекрываются. Они совпадают? Если это так, у вас максимальное совпадение по размеру 2. Продолжайте перемещать верхний лист на 1 символ вправо и каждый раз находите наибольшую часть совпадающих перекрывающихся символов. Всегда отслеживайте ваш самый большой матч. Продолжайте, пока ваш верхний лист не окажется так далеко вправо, что его первый символ будет совпадать с последним символом другого листа.

Я не знаю, насколько легко это было бы реализовать в javascript, но как алгоритм, я думаю, что это нормально.

PS - для бита, где вам нужно найти «самый большой раздел совпадающих, перекрывающихся символов», вы можете сделать что-то вроде этого:

/* Note: str1 and str2 are the two overlapping portions of the strings */
var largestMatch = 0;
var currMatch = 0;
for (var i = 0; i < str1.length; i++) {
    if (str1[i] == str2[i]) currMatch++;
    else currMatch = 0;
    largestMatch = Math.max(largestMatch, currMatch);
}
// largestMatch is the size of the largest section of matched characters
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...