Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
412 views
in Technique[技术] by (71.8m points)

c# - How can I match and return multiple instances of a string, where single apostrophes could be contained at any index?

Please note, the 'C#' tag was included intentionally, because I could accept C# syntax for my answer here, as I have the option of doing this both client-side and server-side. Read the 'Things You May Want To Know' section below. Also, the 'regex' tag was included because there is a strong possibility that the use of regular expressions is the best approach to this problem.

I have the following highlight Plug-In found here:

http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html

And here is the code in that plug-in:

/*

highlight v4

Highlights arbitrary terms.

<http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html>

MIT license.

Johann Burkard
<http://johannburkard.de>
<mailto:jb@eaio.com>

*/

jQuery.fn.highlight = function(pat) {
 function innerHighlight(node, pat) {
  var skip = 0;
  if (node.nodeType == 3) {
   var pos = node.data.toUpperCase().indexOf(pat);
   if (pos >= 0) {
    var spannode = document.createElement('span');
    spannode.className = 'highlight';
    var middlebit = node.splitText(pos);
    var endbit = middlebit.splitText(pat.length);
    var middleclone = middlebit.cloneNode(true);
    spannode.appendChild(middleclone);
    middlebit.parentNode.replaceChild(spannode, middlebit);
    skip = 1;
   }
  }
  else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
   for (var i = 0; i < node.childNodes.length; ++i) {
    i += innerHighlight(node.childNodes[i], pat);
   }
  }
  return skip;
 }
 return this.length && pat && pat.length ? this.each(function() {
  innerHighlight(this, pat.toUpperCase());
 }) : this;
};

jQuery.fn.removeHighlight = function() {
 return this.find("span.highlight").each(function() {
  this.parentNode.firstChild.nodeName;
  with (this.parentNode) {
   replaceChild(this.firstChild, this);
   normalize();
  }
 }).end();
};

This plug-in works pretty easily.

If I wanted to highlight all instances of the word "Farm" within the following element...(cont.)

<div id="#myDiv">Farmers farm at Farmer's Market</div>

...(cont.) all I would need to do is use:

$("#myDiv").highlight("farm");

And then it would highlight the first four characters in "Farmers" and "Farmer's", as well as the entire word "farm" within the div#myDiv

No problem there, but I would like it to use this:

$("#myDiv").highlight("Farmers");

And have it highlight both "Farmers" AND "Farmer's". The problem is, of course, that I don't know the value of the search term (The term "Farmers" in this example) at runtime. So I would need to detect all possibilities of no more than one apostrophe at each index of the string. For instance, if I called $("#myDiv").highlight("Farmers"); like in my code example above, I would also need to highlight each instance of the original string, plus:

  • 'Farmers
  • F'armers
  • Fa'rmers
  • Far'mers
  • Farm'ers
  • Farme'rs
  • Farmer's
  • Farmers'

Instances where two or more apostrophes are found sid-by-side, like "Fa''rmers" should, of course, not be highlighted.

I suppose it would be nice if I could include (to be highlighted) words like "Fa'rmer's", but I won't push my luck, and I would be doing well just to get matches like those found in my bulleted list above, where only one apostrophe appears in the string, at all.

I thought about regex, but I don't know the syntax that well, not to mention that I don't think I could do anything with a true/false return value.

Is there anyway to accomplish what I need here?

Things You May Want To Know:

  1. The highlight plug-in takes care of all the case insensitive requirements I need, so no need to worry about that, at all.
  2. Syntax provided in JavaScript, jQuery, or even C# is acceptable, considering the hidden input fields I use the values from, client-side, are populated, server-side, with my C# code.
  3. The C# code that populates the hidden input fields uses Razor (i.e., I am in a C#.Net Web-Pages w/ WebMatrix environment. This code is very simple, however, and looks like this:

    for (var n = 0; n < searchTermsArray.Length; n++)

    {

    <input class="highlightTerm" type="hidden" value="@searchTermsArray[n]" />
    

    }

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I'm copying this answer from your earlier question.

I think after reading the comments on the other answers, I've figured out what it is you're going for. You don't need a single regex that can do this for any possible input, you already have input, and you need to build a regex that matches it and its variations. What you need to do is this. To be clear, since you misinterpreted in your question, the following syntax is actually in JavaScript.

var re = new RegExp("'?" + "farmers".split("").join("'?") + "'?", "i")

What this does is take your input string, "farmers" and split it into a list of the individual characters.

"farmers".split("") == [ 'f', 'a', 'r', 'm', 'e', 'r', 's' ]

It then stitches the characters back together again with "'?" between them. In a regular expression, this means that the ' character will be optional. I add the same particle to the beginning and end of the expression to match at the beginning and end of the string as well.

This will create a regex that matches in the way you're describing, provided it's OK that it also matches the original string.

In this case, the above line builds this regex:

/'?f'?a'?r'?m'?e'?r'?s'?/

EDIT

After looking at this a bit, and the function you're using, I think your best bet will be to modify the highlight function to use a regex instead of a straight string replacement. I don't think it'll even be that hard to deal with. Here's a completely untested stab at it.

function innerHighlight(node, pat) {
    var skip = 0;
    if (node.nodeType == 3) {
        var matchResult = pat.exec(node.data);  // exec the regex instead of toUpperCase-ing the string
        var pos = matchResult !== null ? matchResult.index : -1;  // index is the location of where the matching text is found
        if (pos >= 0) {
            var spannode = document.createElement('span');
            spannode.className = 'highlight';
            var middlebit = node.splitText(pos);
            var endbit = middlebit.splitText(matchResult[0].length);  // matchResult[0] is the last matching characters.
            var middleclone = middlebit.cloneNode(true);
            spannode.appendChild(middleclone);
            middlebit.parentNode.replaceChild(spannode, middlebit);
            skip = 1;
        }
    }
    else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
        for (var i = 0; i < node.childNodes.length; ++i) {
            i += innerHighlight(node.childNodes[i], pat);
        }
    }
    return skip;
 }

What I'm attempting to do here is keep the existing logic, but use the Regex that I built to do the finding and splitting of the string. Note that I'm not doing the toUpper call anymore, but that I've made the regex case insensitive instead. As noted, I didn't test this at all, but it seems like it should be pretty close to a working solution. Enough to get you started anyway.

Note that this won't get you your hidden fields. I'm not sure what you need those for, but this will (if it's right) take care of highlighting the string.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...