简体   繁体   中英

Regex is not matching all occurrences

I am trying to match all occurrences <% anything %> inside a template literal. My regex setup looks like this.

process(template : string , data: Object) {
    let re = /<%([^%>]+)?%>/g, match;
    while (match = re.exec(template)) {
        template = template.replace(match[0], data[match[1]]);
    }
    return template;
}

if my template looks like this

`<div class="tile">
        <span><%ENTRY_NUMBER%></span> 
        <span><%NAME%> , <%CLASS%> , <%GENDER%> , <%AGE%></span>
</div>`

it will match the first occurrence just fine then keep picking the alternate occurrences. if there is one match inside a html tag it works fine but when i use multiples like in second span element it misses alternate occurrences. I think it has something to do with < and >.

When I separate those occurrences with <> instead of comma like below it works fine.

<span><%NAME%> <> <%CLASS%> <> <%GENDER%> <> <%AGE%></span>

The result it like this

<span> john <> biology <> male <> 17 </span>

I have examined the regex multiple times but to no avail.

The problem is that re.lastIndex stores the value that is correct for the previous value of template , but since you change the template value the re.lastIndex is no longer correct for the modified string.

See a simplified example below:

 let template = `<%ENTRY_NUMBER%><%NAME%><%CLASS%><%GENDER%><%AGE%>`; let re = /<%(.+?)%>/g, match; let data = { 'NAME':'John', 'CLASS':'A3', 'GENDER':'Male', 'AGE':'19' }; while (match = re.exec(template)) { console.log(re.lastIndex); template = template.replace(match[0], data[match[1]]); console.log(template); } console.log(template); // Iteration 1: re.lastIndex = 16 // undefined<%NAME%><%CLASS%><%GENDER%><%AGE%> // Index is now between <%NAME% and > -> this match will be skipped // and so on... 

Thus, it is better to modify the string on the fly inside a replace callback since all matches will get replaced once they are found, and you won't have to track the indices where to search from.

Use

process(template : string , data: Object) {
    return template.replace(/<%(.+?)%>/g, function($0, $1) { 
              return data[$1] ? data[$1] : $0; 
           });
}

Note that <%(.+?)%> matches <% , then captures any 1 or more chars other than line break chars, as few as possible into Group 1 and then matches %> . This way, you will also match values that contain % and > (it is important if data contains such keys).

The problem is you don't need a global flag g becuase you reexecute the expression over and over until will not match but a multiline flag m

 let s = `<div class="tile"> <span><%ENTRY_NUMBER%></span> <span><%NAME%> , <%CLASS%> , <%GENDER%> , <%AGE%></span> </div>`; const re = /<%([^\\%]+)?%>/, data = { ENTRY_NUMBER: '1977', NAME: 'Blue', CLASS: 'Vertebrates', GENDER: 'Male', AGE: '40' }; let k = 0; while (re.test(s) && k++ < 1000) { match = re.exec(s); if (match) s = s.replace(match[0], data[match[1]]); } console.log(s); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM