Javascript 正则表达式不匹配部分序列,但匹配完整序列

Javascript 正则表达式不匹配部分序列,但匹配完整序列,javascript,regex,Javascript,Regex,我有一些转义HTML,如下所示: <img border='0' /> 我试图匹配并替换完整的转义序列,如'但不是部分的,如39,因为39实际上不在未转换字符串中。本质上,每个转义序列都应该被视为一个标记 这是一个JS正则表达式。是否有方法排除&和之间的匹配同时仍接受包含这两个字符的序列 预期结果: 搜索img边框=';0' /对于lt:不匹配 搜索img边框=';0' /对于39:不

我有一些转义HTML,如下所示:

<img border='0' />
我试图匹配并替换完整的转义序列,如
'但不是部分的,如
39
,因为
39
实际上不在未转换字符串中。本质上,每个转义序列都应该被视为一个标记

这是一个JS正则表达式。是否有方法排除
&
之间的匹配同时仍接受包含这两个字符的序列

预期结果:

  • 搜索
    img边框=';0' /对于
    lt
    :不匹配
  • 搜索
    img边框=';0' /对于
    39
    :不匹配
  • 搜索
    img边框=';0' /用于
    和#039:匹配
  • 搜索
    img边框=';0' /用于
    border=':匹配
当前代码:

> var str = '<img border='0' />'
> str.replace(/(border)/gi, '|$1|')
'<img |border|='0' />'  // ok
> str.replace(/(39)/gi, '|$1|')
'<img border=&#0|39|;0&#0|39|; />'  // not ok

注意:我不能先退出,然后再重新退出以匹配。它必须被转义。

我认为您指的是非捕获组:,这在一些堆栈溢出帖子(和)中得到了轻松解决


也就是说,未捕获的组没有自己的组选择器(例如/a(?[X-Z])([a-c])/g将匹配“aZb”,但\1将等于“b”,而不是“Z”。

这就是您试图做的吗

var str=“img边框颜色=';0';”
console.log(str)
console.log(str.match(/((?:[a-z-]+=)?&#.+?)/gi))

log(str.replace(/((?:[a-z-]+=)?&#.+?)/gi,“|$1 |”)
这里的一个选项是在执行实际替换之前,在转义字符序列中的任何位置临时将正在搜索的字符串替换为“伪”字符串字符串必须是HTML中不太可能出现的内容。执行实际替换后,可以进行进一步替换,将“伪”字符串更改回正在搜索的字符串

下面是此方法的一个演示,它将生成所请求的结果。当需要不使用正则表达式的全局替换时,它将使用该方法,并将任何字符串转换为字符串文字以用于正则表达式(任何特殊字符都将被适当转义)

var html=“img border=';0';/”
replaceInHtml(html,'lt','replacement');
replaceInHtml(html,'39','replacement');
replaceInHtml(html,';,'replacement');
replaceInHtml(html,'border=';','replacement');
函数replaceInHtml(html、str、replacement){
//不太可能出现在HTML中的唯一字符串
变量dummyStr='!*&$^;';
var strInRegex=escapeRegExp(str);
var dummyRegex=新的RegExp(“(&[#a-zA-Z0-9]*)”
+strInRegex+'([#a-zA-Z0-9]*)','g');
var replaced=html.replace(dummyRegex,'$1'+dummyStr+'$2');
替换=替换。拆分(str)。连接(替换);
已替换=已替换.split(dummyStr).join(str);
console.log('Source:'+html
+'\n替换:'+str
+“\n带:”+替换
+“\n活动:”+已替换);
}
函数escapeRegExp(str){
返回str.replace(/[\-\[\]\/\{\\}(\)\*\+\?\.\\\^\$\\\\\\\\\\;]/g,“\$&”);

}
我从匹配
&
之间的所有内容开始;

let str=“39img border=39';0';&39;/39”;
让搜索='39';
设regexp=newregexp('&[^&;]*?('+search+')[^&;]*?;','g');//&[^&;]*?(search)[^&;]*?;/g
让match=str.match(regexp);

console.log(match);
我认为如果我们能够使用回溯,这是可能的。鉴于正则表达式的风格是JavaScript,我认为我们不能。这非常接近:
[^&;]*(string)[^&]*(?!9;| t;|))

应该使用正则表达式来检查这一点,但它不能覆盖所有可能的实体,并且不是此作业的最佳工具。而下面的方法将适用于所有HTML实体

我试图匹配和替换完整的转义序列,如
';
,但不是部分的,如
39
,因为
39
实际上不在未转义字符串中

基本上,您希望用它的未定型形式替换HTML实体。下面的函数就是这么做的,您不需要正则表达式

我将从中使用
unescapethtml
函数

这首先创建一个新的
元素。在函数中,作为参数传递的字符串随后被指定为此textarea的innerHTML,然后返回其textContent。这是用于取消显示HTML实体的技巧

我们可以重用它来确定字符串是否为有效的HTML实体。如果函数能够对其进行取消扫描,则它是有效的HTML实体,否则它不是。这就是您要确定的

var escape=document.createElement('textarea');
函数unescapethtml(html){
escape.innerHTML=html;
返回escape.textContent;
}
var str='img border=';0';/';
log(unescapeHTML('lt')!='lt');
log(unescapeHTML('39')!='39');
log(unescapeHTML('';')!='';');
log(unescapeHTML('border=';')!='border=';');
最终候选版本

4/29

此版本应处理搜索字符串末尾的部分实体
其中,部分具有前实体字符,如
xxx&yyy
a�

这是@TomasLangkaas发现的最后一个病例。
鉴于涵盖了所有其他案例,这是最终版本候选
为@Athanchahill或其他感兴趣的人

(参见注释和以前的版本)

模型已从String.Replace()更改为while(match=Rx.exec())

此处解释,但请参见JS代码以了解实现。
它仍然使用搜索字符串作为第一个替代项
以实体作为第二个

    (?=
         # This is the optional entity captured at
         # the same position where the search string starts.
         # If this entity matches, it means the search string
         # matches. Either one may be a partial of the other.

         # (1) The container for pre-entity / entity
         (                             
              # (2) Pre-entity characters 
              ( sLongest )                

              # Entity   
              (?:&(?:[a-z_:][a-zd_:.-]*|(?:\#(?:[0-9]+|x[0-9a-f]+)))|%[a-z_:][a-zd_:.-]*);
         )?                            
    )

    # (3) The search string ( consumes )
    ( sToFind )                        
 | 

    # (4) Or, the entity last  ( consumes ) 
    ( (?:&(?:[a-z_:][a-zd_:.-]*|(?:\#(?:[0-9]+|x[0-9a-f]+)))|%[a-z_:][a-zd_:.-]*); )
请注意,不能将实体语法分解为正则表达式的一部分。
它必须完全匹配,作为一个独特的
    (?=
         # This is the optional entity captured at
         # the same position where the search string starts.
         # If this entity matches, it means the search string
         # matches. Either one may be a partial of the other.

         # (1) The container for pre-entity / entity
         (                             
              # (2) Pre-entity characters 
              ( sLongest )                

              # Entity   
              (?:&(?:[a-z_:][a-zd_:.-]*|(?:\#(?:[0-9]+|x[0-9a-f]+)))|%[a-z_:][a-zd_:.-]*);
         )?                            
    )

    # (3) The search string ( consumes )
    ( sToFind )                        
 | 

    # (4) Or, the entity last  ( consumes ) 
    ( (?:&(?:[a-z_:][a-zd_:.-]*|(?:\#(?:[0-9]+|x[0-9a-f]+)))|%[a-z_:][a-zd_:.-]*); )
function do_replace(test_str, find_str, replace_str) {
        let escaped_find_str = AmpExcape(find_str);
        escaped_find_str = RegExcape(escaped_find_str);
        let escaped_replace_str = RegExcape(replace_str);

        let first_regex = new RegExp('(' + escaped_find_str + ')|(&\\w*;|&#[0-9a-fA-F]*;)','gi');
        let first_pass = test_str.replace(first_regex,'&&$1'+replace_str+'&&$2');
        let second_regex = new RegExp('&&(?:'+escaped_replace_str+'&&|(' + escaped_find_str + ')?('+escaped_replace_str + ')?&&)','gi');
        let second_pass = first_pass.replace(second_regex,'$2');

        return second_pass;
}
&&<target group value><replacement string>&&<entity group value>
// helper for building regexes from strings
// http://stackoverflow.com/a/3561711/6738706
function AmpExcape(str) {
        return str.replace(/&(\w*;|#[0-9a-fA-F]*;)|(&)/g, '&$2$1');
}
function RegExcape(str) {
        return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
function do_replace(test_str, find_str, replace_str) {
        let escaped_find_str = AmpExcape(find_str);
        escaped_find_str = RegExcape(escaped_find_str);
        let escaped_replace_str = RegExcape(replace_str);

        let first_regex = new RegExp('(' + escaped_find_str + ')|(&\\w*;|&#[0-9a-fA-F]*;)','gi');
        let first_pass = test_str.replace(first_regex,'&&$1'+replace_str+'&&$2');
        let second_regex = new RegExp('&&(?:'+escaped_replace_str+'&&|(' + escaped_find_str + ')?('+escaped_replace_str + ')?&&)','gi');
        let second_pass = first_pass.replace(second_regex,'$2');

        return second_pass;
}
let str = '39&lt;img style=&#039;width: 39;&#039; bor9;wder=39&#039;0&#039;&39; /&gt;39;';
let test_list = ['39','39;','9;','9;w','39&','0&#'];
run_test(str,test_list);

str = '&lt;img border=&#039;0&#039; /$gt;';
test_list = ['lt','39','&#039;','border=&#039;'];
run_test(str,test_list);

str = 'test string ring ring';
test_list = ['ring'];
run_test(str,test_list);

str = '39&lt;img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;';
test_list = ['border','0','&#039;','39','lt','&lt;','border=&#039;','&lt;img','&g','t;'];
run_test(str,test_list);

function run_test(base_str, find_list) {

        let orig_str = 'original';

        let max_len = find_list.concat(orig_str).reduce(function(a,b) {
                return a > b.length ? a : b.length;
        },0);
        console.log();
        console.log(pad(orig_str,max_len) + ': ' + str);

        find_list.map(function(gstr) {
                console.log( pad( gstr, max_len) + ': ' + do_replace(str, gstr, '|' + gstr + '|'));
        });
}
function pad(str,len) {
        while ( str.length < len) { str = str + ' ' };
        return str;
}
original: 39&lt;img style=&#039;width: 39;&#039; bor9;wder=39&#039;0&#039;&39; /&gt;39;
39      : |39|&lt;img style=&#039;width: |39|;&#039; bor9;wder=|39|&#039;0&#039;&39; /&gt;|39|;
39;     : 39&lt;img style=&#039;width: |39;|&#039; bor9;wder=39&#039;0&#039;&39; /&gt;|39;|
9;      : 39&lt;img style=&#039;width: 3|9;|&#039; bor|9;|wder=39&#039;0&#039;&39; /&gt;3|9;|
9;w     : 39&lt;img style=&#039;width: 39;&#039; bor|9;w|der=39&#039;0&#039;&39; /&gt;39;
39&     : 39&lt;img style=&#039;width: 39;&#039; bor9;wder=39&#039;0&#039;&39; /&gt;39;
0&#     : 39&lt;img style=&#039;width: 39;&#039; bor9;wder=39&#039;0&#039;&39; /&gt;39;

original     : &lt;img border=&#039;0&#039; /$gt;
lt           : &lt;img border=&#039;0&#039; /$gt;
39           : &lt;img border=&#039;0&#039; /$gt;
&#039;       : &lt;img border=|&#039;|0|&#039;| /$gt;
border=&#039;: &lt;img |border=&#039;|0&#039; /$gt;

original: test string ring ring
ring    : test st|ring| |ring| |ring|

original     : 39&lt;img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
border       : 39&lt;img style=&#039;width: 39;&#039; |border|=&#039;0&#039;&39; /&gt;39;
0            : 39&lt;img style=&#039;width: 39;&#039; border=&#039;|0|&#039;&39; /&gt;39;
&#039;       : 39&lt;img style=|&#039;|width: 39;|&#039;| border=|&#039;|0|&#039;|&39; /&gt;39;
39           : |39|&lt;img style=&#039;width: |39|;&#039; border=&#039;0&#039;&39; /&gt;|39|;
lt           : 39&lt;img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
&lt;         : 39|&lt;|img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
border=&#039;: 39&lt;img style=&#039;width: 39;&#039; |border=&#039;|0&#039;&39; /&gt;39;
&lt;img      : 39|&lt;img| style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
&g           : 39&lt;img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
t;           : 39&lt;img style=&#039;width: 39;&#039; border=&#039;0&#039;&39; /&gt;39;
'((?:^|(?!(?:[^&;]+' + str + ')))(?=(?:(?:&|;|^)[^&;]*))(?:(?!(?:&))(?:(?:^|[;]?)[^&;]*?))?)(' + str + ')'
"original":      &lt;img border=&#039;0&#039; /$gt;
"lt":            &lt;img border=&#039;0&#039; /$gt;
"39":            &lt;img border=&#039;0&#039; /$gt;
"&#039;":        &lt;img border=|&#039;|0|&#039;| /$gt;
"border=&#039;": &lt;img |border=&#039;|0&#039; /$gt;
"original":      39&lt;img style=&#039;width: 39;&#039; bor;der=39&#039;0&#039;&39; /&gt;39;
test string that may be followed by semi-colon :
|39|&lt;img style=&#039;width: |39|;&#039; bor;der=|39|&#039;0&#039;&39; /&gt;|39|;
test match with semi-colon:
39&lt;img style=&#039;width: |39;|&#039; bor;der=39&#039;0&#039;&39; /&gt;|39;|
test match with semi-colon mid string
39&lt;img style=&#039;width: 39;&#039; |bor;der|=39&#039;0&#039;&39; /&gt;39;
"original":      test string ring ring
test st|ring| ring ring
function make_regex(str) {
  let regexp = new RegExp('((?:^|(?!(?:[^&;]+' + str + ')))(?=(?:(?:&|;|^)[^&;]*))(?:(?!(?:&))(?:(?:^|[;]?)[^&;]*?))?)(' + str + ')','gi');
  return regexp;
}

function do_replace(test_str, find_str, replace_str) {
        let new_reg = make_regex(find_str);
        return  test_str.replace(new_reg,'$1' + replace_str);
}

let str = '39&lt;img style=&#039;width: 39;&#039; bor;der=39&#039;0&#039;&39; /&gt;39;';
console.log();
console.log('"original":     ', str);
console.log('test string that may be followed by semi-colon :');
console.log(do_replace(str, '39', '|$2|' ));
console.log('test match with semi-colon:');
console.log(do_replace(str, '39;', '|39;|' ));
console.log('test match with semi-colon mid string');
console.log(do_replace(str, 'bor;der', '|bor;der|' ))

str = '&lt;img border=&#039;0&#039; /$gt;';
console.log();
console.log('"original":     ', str);
console.log('"lt":           ', do_replace(str, 'lt', '|lt|' ));
console.log('"39":           ', do_replace(str, '39', '|39|' ));
console.log('"&#039;":       ', do_replace(str, '&#039;', '|&#039;|' ));
console.log('"border=&#039;":', do_replace(str, 'border=&#039;', '|border=&#039;|' ));
str = 'test string ring ring';
console.log();
console.log('"original":     ', str);
console.log(do_replace(str, 'ring', '|$2|')); 
(?:^|(?!(?:[^&;]+' + str + ')))
(?=(?:(?:&|;|^)[^&;]*))
(?:(?!(?:&))(?:(?:^|[;]?)[^&;]*?))?
str.replace(
  /(&[a-z]+;|&#[0-9a-f]+;)|39/gi,
  function(m, entity){
    return entity || replacement;
  }
);
/&[a-z]+;|&#x[a-f\d]+;|&#\d+;/gi
function searchAndReplace(searchFor, replacement, str) {
  return str.replace(
    new RegExp(
      prepare(searchFor) + 
      "|(&[a-z]+;|&#x[a-f\\d]+;|&#\\d+;)", // consume entities
      "gi"
    ),
    function(m, entity) {
      return entity || replacement;
    }
  );
}

function prepare(str) {
  return str.replace(/[^\w\s]/g, "\\$&"); //escape regex metachars [1]
}

// [1] from http://eloquentjavascript.net/09_regexp.html#h_Rhu25fogrG