另一个字符串中的javascript字符串自动换行

时间:2011-06-15 14:41:52

标签: javascript jquery string

如果我有一个字符串输入,我想要与另一个字符串进行比较,并使用最大可能的匹配将输入字符串的匹配包装在另一个字符串中。我怎样才能最好地将匹配包装在标签中?这是一个非常重要的问题。

基本上,我想将输入的字符串与另一个字符串匹配,使用span标记显示在输入的字符串中找到的匹配的目标部分。

  • 首先从输入字符串的开头匹配(最大可能匹配)
  • 应突出显示搜索词的部分匹配(请参阅“barge”,“示例中的驳船”)
  • 特殊字符符应匹配“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 );
};

这不符合“最大匹配”的要求。我希望在包装字符串中出现匹配字符串的任何部分的最大可能匹配。

使用纯javascript或jquery或组合的解决方案是可以接受的。

编辑:有些人建议使用KMP,这里是KMP jsfiddle.net/y5yJY/2 的示例,但它没有,它的当前形式适合所有标准并进行单一匹配。

3 个答案:

答案 0 :(得分:2)

我有一个有趣的解决方案应该可以作为您的原始规格。它没有经过压力测试,我不确定你是否要处理大量的文本,并且它会进行相当多的正则表达式匹配。不一定是最干净或最简单的解决方案,但它可以实现预期。

功能和限制:

  • 它处理匹配字符串中的大多数奇怪情况,例如重复的单词,非常相似或重复的短语等。

  • 目前,您无法在源字符串中可靠地拥有[]个字符,因为它们是在内部使用的。如果这是一个问题,你必须在匹配之前将它们交换到任何其他字符或字符组合。

  • 对于N个字词的匹配字符串,2*N + 5字符串替换是使用不同复杂程度的正则表达式完成的。

  • 它匹配不区分大小写的单词和短语,忽略任何非单词字符。同时,它会在结果中保留混合大小写单词和非单词单词。

工作原理:

  1. 首先,它分别查找每个单词,并将其匹配字符串中的索引附加到方括号中:word[2]。如果单词出现多次,则会附加所有索引:word[3][2][1]

  2. 接下来,它通过查看周围单词的索引来查找并标记不在包装边界上的单词。在单独的步骤中,它从这些单词中删除索引。最后one[1] two[2] three[3]将成为one[1] []two three[3]

  3. 现在剩下的就是按照一定的顺序做一些假设,并包装单词/短语。查看代码以查看已完成的所有替换。

  4. 重要的是,在第一步之后,我们永远不会直接匹配单词,从那时起,单词就被称为any number of word characters before [index]any number of word characters after []。这可以确保我们正确地包装重复的单词/短语。

    看看this demo。我添加了一个悬停效果,因此您可以看到哪些单词被分组并包装在一起。

    这是疯狂的代码,享受!

    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');
    

    正则表达式中的\b表示匹配单词边界,即单词字符序列的开头或结尾。这就是为什么上面的\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将匹配endangercat将匹配cats,但can将不匹配cannot,因为这将是3个额外的字母

    匹配复杂的复数形式和不规则动词并不容易,您可以在服务器上构建一个包含不规则单词的大量字典并预处理该单词,因此如果用户输入foot,则使用正则表达式{{1将匹配两个表单。更简单的解决方案是只关心常规单词。对于大多数单词,匹配\b(foot|feet)\b将足以捕捉复数,它也匹配\bword(s|es|)\bwordwords。对于像wordes这样的字词,正则表达式fly将完成这项工作。对于\bfl(y|ies)\b这样的字词,正则表达式index将匹配最常见的表单。

    由于我不是真正的语言专家,我现在就把它留在那里。

    输入中的通配符

    与上面类似,在输入字符串中添加对通配符的支持非常容易。假设我们想让用户输入\bind(ex|exes|ices)\b来表示任何字符。如果输入为?,我们只需要在正则表达式中将?red替换为?。例如,\w也会匹配\b\wred\bfred

    如上所述,您还可以使用多个通配符,将dred替换为一个或多个字符\w+ 零个或多个字符< / em>的。 \w*将匹配\bf\w+d\bfedfeed也会匹配\w*

答案 1 :(得分:1)

这个怎么样:(仅描述算法,不用代码编写)

想象一下,你有两张纸写在两张纸上。放置两张纸,使一张在另一张纸之上。将顶部纸张向左移动,使其最后一个字母位于底部工作表的第一个字母的顶部。现在,这两个重叠的字母是否匹配?如果是这样,你有一个长度为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

答案 2 :(得分:1)

以下是我为解决这个问题所采取的措施:(寻求改进,因为它并不完美) (这包含在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);

需要改进的地方:

  • 使用其他字符来分隔单词,而不仅仅是空格。
  • 提高效率
  • 在“搜索”和“匹配”字符串中更好地检测字符串的“块”(两个或多个单词)并处理它们。