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.