6

I'm using JavaScript to do some regular expression. Considering I'm working with well-formed source, and I want to remove any space before[,.] and keep only one space after [,.], except that [,.] is part of a number. Thus I use:

text = text.replace(/ *(,|\.) *([^ 0-9])/g, '$1 $2');

The problem is that this replaces also text in the html tag attributes. For example my text is (always wrapped with a tag):

<p>Test,and test . Again <img src="xyz.jpg"> ...</p>

Now it adds a space like this src="xyz. jpg" that is not expected. How can I rewrite my regular expression? What I want is

<p>Test, and test. Again <img src="xyz.jpg"> ...</p>

Thanks!

bakkal
  • 54,350
  • 12
  • 131
  • 107
jcisio
  • 509
  • 1
  • 7
  • 17
  • 9
    This isn't something Regex's are good at as HTML isn't a regular language. There is too much scope/nesting/context. – CaffGeek Aug 11 '10 at 15:26
  • 1
    Is that text accessibly through the DOM? – Gumbo Aug 11 '10 at 15:39
  • Yes, I think, even I haven't tried. I wanted to write it as a CKEditor plugin, that's why I said "well-formed" (well, I meant XHTML anyway). I have the source code, but I think I can get is as DOM elements. – jcisio Aug 13 '10 at 08:10

6 Answers6

4

You can use a lookahead to make sure the match isn't occurring inside a tag:

text = text.replace(/(?![^<>]*>) *([.,]) *([^ \d])/g, '$1 $2');

The usual warnings apply regarding CDATA sections, SGML comments, SCRIPT elements, and angle brackets in attribute values. But I suspect your real problems will arise from the vagaries of "plain" text; HTML's not even in the same league. :D

Alan Moore
  • 73,866
  • 12
  • 100
  • 156
  • 1
    I doesn't work. "Test,and" should become "Test, and". I was thinking of lookafter too, but I couldn't get it. Something like looking for "...> anything but < (text to find/replace)". And I think the [^<>]* part above is not necessary. – jcisio Aug 13 '10 at 08:21
  • There more asterisks in there when I tested it, but they disappeared. Try it now. – Alan Moore Aug 14 '10 at 04:00
  • I was using another solution. But this one is much better :) Thanks. – jcisio Feb 01 '11 at 14:04
  • @AlanMoore can you kindly provide a regex to also take care of any Search pattern provided.... – Rusty Sep 28 '21 at 19:54
1

Do not try to rewrite your expression to do this. You won’t succeed and will almost certainly forget about some corner cases. In the best case, this will lead to nasty bugs and in the worst case you will introduce security problems.

Instead, when you’re already using JavaScript and have well-formed code, use a genuine XML parser to loop over the text nodes and only apply your regex to them.

scy
  • 7,132
  • 2
  • 27
  • 35
1

If you can access that text through the DOM, you can do this:

function fixPunctuation(elem) {
    // check if parameter is a an ELEMENT_NODE
    if (!(elem instanceof Node) || elem.nodeType !== Node.ELEMENT_NODE) return;
    var children = elem.childNodes, node;
    // iterate the child nodes of the element node
    for (var i=0; children[i]; ++i) {
        node = children[i];
        // check the child’s node type
        switch (node.nodeType) {
        case Node.ELEMENT_NODE:
            // call fixPunctuation if it’s also an ELEMENT_NODE
            fixPunctuation(node);
            break;
        case Node.TEXT_NODE:
            // fix punctuation if it’s a TEXT_NODE
            node.nodeValue = node.nodeValue.replace(/ *(,|\.) *([^ 0-9])/g, '$1 $2');
            break;
        }
    }
}

Now just pass the DOM node to that function like this:

fixPunctuation(document.body);
fixPunctuation(document.getElementById("foobar"));
Gumbo
  • 643,351
  • 109
  • 780
  • 844
0

Html is not a "regular language", therefore regex is not the optimal tool for parsing it. You might be better suited to use a html parser like this one to get at the attribute and then apply regex to do something with the value.

Enjoy!

Doug
  • 5,268
  • 24
  • 31
0

Don't parse regexHTML with HTMLregex. If you know your HTML is well-formed, use an HTML/XML parser. Otherwise, run it through Tidy first and then use an XML parser.

Community
  • 1
  • 1
Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
0

As stated above and many times before, HTML is not a regular language and thus cannot be parsed with regular expressions.

You will have to do this recursively; I'd suggest crawling the DOM object.

Try something like this...

function regexReplaceInnerText(curr_element) {
    if (curr_element.childNodes.length <= 0) { // termination case:
                                               // no children; this is a "leaf node"
        if (curr_element.nodeName == "#text" || curr_element.nodeType == 3) { // node is text; not an empty tag like <br />
            if (curr_element.data.replace(/^\s*|\s*$/g, '') != "") { // node isn't just white space
                                                                     // (you can skip this check if you want)
                var text = curr_element.data;
                text = text.replace(/ *(,|\.) *([^ 0-9])/g, '$1 $2');
                curr_element.data = text;
            }
        }
    } else {
        // recursive case:
        // this isn't a leaf node, so we iterate over all children and recurse
        for (var i = 0; curr_element.childNodes[i]; i++) {
            regexReplaceInnerText(curr_element.childNodes[i]);
        }
    }
}
// then get the element whose children's text nodes you want to be regex'd
regexReplaceInnerText(document.getElementsByTagName("body")[0]);
// or if you don't want to do the whole document...
regexReplaceInnerText(document.getElementById("ElementToRegEx"));
Richard JP Le Guen
  • 28,364
  • 7
  • 89
  • 119