正则表达式即使使用%%和\\也能正确替换对%s,\ s

时间:2016-02-03 15:25:05

标签: regex

我想找到一个正则表达式,用我的自定义字符串%%替换% %sfoobar。这听起来很诡异,因为它应该将%%s变成%s不会 %foobar,所以这个天真的实现不起作用:

s/%%/%/g
s/%s/foobar/g

这个问题非常普遍,我在编程生涯中多次遇到过这个问题。不仅仅是百分比或百分比逃逸,还有反斜杠字符或反弹逃逸。我将发布我的常用解决方案,但我想知道是否有更好的方法。

(请允许我为将来的搜索做一些关键字填充:字符对,反斜杠反斜杠,反斜杠x,百分比,百分比s。谢谢。)

如果有特定的语言功能有助于此用例,我有兴趣了解它们是什么。

输入和输出示例:

input  : test %%, test %s, test %%s too
output : test %, test foobar, test %s too

另一个:

input  : test%%,test%s,test%%stoo
output : test%,testfoobar,test%stoo

5 个答案:

答案 0 :(得分:1)

这可以简化,可以在单replace次电话中完成:

var str = "test %s, test %%, test %%s too";
var output = str.replace(/%%|(%s)/g, function($0, $1){
     return $1!==undefined?'foobar':'%'; });
//=> test foobar, test %, test %s too

我们首先使用替换/%%/(%s)/并在匹配(%s)时使用捕获组。在replace callback中,我们使用$1!==undefined来决定要用作替代者的字符串。

答案 1 :(得分:1)

正则表达式是 - 如果你运行它们两次,它们会被应用两次。

所以是的 - 你的实施不会起作用,因为你要搜索两次' - 在你第一次更换之后,你无法区分它们。

那么呢?

#!/usr/bin/env perl

use strict;
use warnings;

my %replace = ( '%%' => '%',  
                '%s' => 'foobar' );

my $search = join ( "|", keys %replace );
   $search = qr/($search)/; 

print "Search regex: $search\n";
while ( <DATA> ) {
   s/$search/$replace{$1}/g;
   print;
}

##output : test %, test foobar, test %s too
##output : test%,testfoobar,test%stoo

__DATA__
test %%, test %s, test %%s too
test%%,test%s,test%%stoo

这样做很有意思,但是你正在构建一个查找表 - 捕获左侧,并在右侧查找它应该替换的内容。 (你也可以把它变成一个衬里)。

输出:

Search regex: (?^:(%%|%s))
test %, test foobar, test %s too
test%,testfoobar,test%stoo

非常确定你应该能够在大多数语言中实现这一点。

作为替代方案,它可能值得考虑正则表达式lookaround让你 - 如果你以相反的顺序做正则表达式:

#!/usr/bin/env perl

use strict;
use warnings;

while ( <DATA> ) {
   s/(?<!%)%s/foobar/g;
   s/%%/%/g;
   print;
}

##output : test %, test foobar, test %s too
##output : test%,testfoobar,test%stoo

__DATA__
test %%, test %s, test %%s too
test%%,test%s,test%%stoo

(?<!%)是一个零宽度断言,表示不会出现百分比&#39; - 所以它贯穿并用&#34; foobar&#34;替换 %s。 (但忽略%%s)。然后应用二次转换,它不会捕捉到foobar&#39; foobar&#39;因为那里没有%%

输出:

test %, test foobar, test %s too
test%,testfoobar,test%stoo

这种方法的缺点是并非所有语言都能正确支持环顾四周。 (这是一个先进的正则表达式&#39;事情,而不是&#39;基本&#39;)

答案 2 :(得分:1)

通过正则表达式替换不能最佳地解决转义序列的一般问题。

您必须将您的字符串视为由状态机以词汇方式评估的标记序列。

首先处于NORMAL状态 在正常状态下,您遇到的任何字符都会按原样复制到输出中,除非它是%,在这种情况下,您输入状态PERCENT。 在该州,您可以遇到%,然后输出%并返回NORMAL
您还可以遇到s,然后弹出下一个替换字符串,输出它,然后返回NORMAL
最后,根据您需要的行为,PERCENT状态中遇到的任何其他字符都可能产生错误,或被忽略......

示例javascript代码:

function parseString(s, vars) {
    var NORMAL = 0, PERCENT = 1;

    var state = NORMAL;
    var varidx = 0;
    var output = '';
    for (var i = 0; i <  s.length; i++) {
        if (state == NORMAL) {
            if (s[i] == '%') {
                state = PERCENT;
            } else {
                output += s[i];
            }
        } else if (state == PERCENT) {
            if (s[i] == '%') {
                output += s[i];
                state = NORMAL;
            } else if (s[i] == 's') {
                output += vars[varidx++];
                state = NORMAL;
            } else {
                throw 'Invalid syntax';
            }
        }
    }
    return output;
}

示例:

parseString("test %%, test %s, test %%s too", ['foo']);
// returns "test %, test foo, test %s too"

虽然这种方法的代码比基于regexp的解决方案更多,但它可能更快,因为正则表达式涉及更高程度的复杂性,并且它允许您以最适合您的方式处理无效语法。

答案 3 :(得分:0)

这是一种简单的方法。使用正则表达式/%%|%s|./将字符串拆分为一个块数组,因为缺少更好的术语,以便每个块都是一个字符或转义字符,然后检查每个单独的块%s和{ {1}}像这样,进行unescaping,再次加入数组,如下所示:

%%

Javascript中的相同想法没有使用变量来保存块:

Input  : "test %s, test %%, test %%s too"
Array  : ["t", "e", "s", "t", " ", "%s", ",", " ", "t", "e", "s", "t", " ", "%%",
          " ", "t", "e", "s", "t", "%%", "s", " ", "t", "o", "o"]
Output : "test foobar, test %, test %s too"

答案 4 :(得分:0)

%% %方式将%s替换为foobar,将string.replace(/%%|%s/g, function (match) { // If %% is matched, replace it by % // else %s is matched, replace by `foobar` return match === '%%' ? '%' : 'foobar'; }); 替换为var str = "test %s, test %%, test %%s too"; str = str.replace(/%%|%s/g, function (_) { return _ === '%%' ? '%' : 'foobar'; }); console.log(str); document.body.innerHTML = str;

str
    .replace(/%%/g, '2percent') // Replace first string by some string that will not possibly be appear/present in the main string
    .replace(/%s/g, 'foobar') // Replace second string
    .replace(/2percent/g, '%'); // Replace temp by the normal string

var str = "test %s, test %%, test %%s too";

str = str
    .replace(/%%/g, '2percent')
    .replace(/%s/g, 'foobar')
    .replace(/2percent/g, '%');

console.log(str);
document.body.innerHTML = str;

使用此方法,仅使用一次,而不是其他答案的三倍。

这是使用临时变量的变量交换逻辑的另一种方法。 与String#replace

this answer类似
search/list

part=snippet