Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Regex查找(/replace)字符串中字符的多个实例_Regex_Perl_Replace - Fatal编程技术网

Regex查找(/replace)字符串中字符的多个实例

Regex查找(/replace)字符串中字符的多个实例,regex,perl,replace,Regex,Perl,Replace,关于如何构造(perl)正则表达式,perl-pe的///g;',它将查找/替换指定字符串中给定字符/字符集的多个实例。起初,我以为g“全球”标志会做到这一点,但我显然误解了这里非常重要的一点: 例如,我想消除特定字符串中的任何非字母数字字符(在较大的文本语料库中)。举个例子,字符串的标识方式是以[后跟@开头,可能中间有一些字符 [abc@def"ghi"jkl'123] 下面的正则表达式 s/(\[[^\[\]]*?@[^\[\]]*?)[^a-zA-Z0-9]+?([^\[\]]*?)/$

关于如何构造(perl)正则表达式,
perl-pe的///g;',它将查找/替换指定字符串中给定字符/字符集的多个实例。起初,我以为g“全球”标志会做到这一点,但我显然误解了这里非常重要的一点:

例如,我想消除特定字符串中的任何非字母数字字符(在较大的文本语料库中)。举个例子,字符串的标识方式是以[后跟@开头,可能中间有一些字符

[abc@def"ghi"jkl'123]
下面的正则表达式

s/(\[[^\[\]]*?@[^\[\]]*?)[^a-zA-Z0-9]+?([^\[\]]*?)/$1$2/g;
将找到第一个“如果我运行它三次,我将获得所有三次。 类似地,如果我想用其他字符替换非字母数字字符,比如X,该怎么办

s/(\[[^\[\]]*?@[^\[\]]*?)[^a-zA-Z0-9]+?([^\[\]]*?)/$1X$2/g; 

这个技巧只适用于一个实例。但是我如何能一次找到所有这些方法呢?

因此,我将建议一种计算效率极低的方法。效率极低,但可能比
可变长度查找更快,而且也很容易(对您而言):

\K
会删除它之前的所有内容……因此只有它之后的字符才被实际替换

perl -pe 'while (s/\[[^]]*@[^]]*\K[^]a-zA-Z0-9]//){}' file
基本上,我们只有一个空循环,直到搜索和替换什么都不替换为止

略为改进的版本:

perl -pe 'while (s/\[[^]]*?@[^]]*?\K[^]a-zA-Z0-9](?=[^]]*?])//){}' file

(?=)
验证其内容在匹配后是否存在,而不属于匹配的一部分。这是一个
可变长度的前瞻(我们缺少的是另一个方向)。我还使
*
延迟,因此我们可以获得尽可能短的匹配。

这里是另一种方法。精确捕获需要工作的子字符串,并在替换部分运行一个正则表达式,清除其中的非字母数字字符

use warnings;
use strict;
use feature 'say';

my $var = q(ah [abc@def"ghi"jkl'123] oh); #'
say $var;

$var =~ s{ \[ [^\[\]]*? \@\K ([^\]]+) }{
    (my $v = $1) =~ s{[^0-9a-zA-Z]}{}g;
    $v
}ex;

say $var;
如果需要单独的
$v
来返回该值而不是匹配数,
s/
运算符本身返回的值。这可以通过使用
/r
修饰符来改进,修饰符返回更改的字符串,而不更改原始字符串(因此它不会尝试更改
$1
,这是不允许的)

\K
的存在使得“删除”之前的所有匹配项都不会被消耗,因此我们不需要捕获它们以将其放回。修改器使替换零件作为代码进行评估

问题中的代码不起作用,因为匹配的所有内容都已被消耗,并且(在
/g
下)搜索将从上次匹配后的位置继续,尝试再次在字符串的下一个位置查找整个模式。这将失败,仅替换第一个匹配项

perl -pe 'while (s/\[[^]]*@[^]]*\K[^]a-zA-Z0-9]//){}' file

我们希望保留在字符串中的匹配问题通常可以通过
\K
解决(在所有当前答案中使用),这样就不会使用之前的所有匹配项。

代码不起作用的原因是,
/g
在替换后不会重新扫描字符串。它会查找给定正则表达式的所有非重叠匹配项,然后替换中的替换部分

[abc@def“ghi”jkl'123]
,只有一个匹配项(即
[abc@def“
字符串的一部分,带有
$1=”[abc@def“
$2=”
),因此只删除第一个

在第一次匹配之后,Perl扫描剩余的字符串(
ghi“jkl'123]
)寻找另一个匹配,但没有找到另一个
[
(或
@


我认为最简单的解决方案是使用嵌套的搜索/替换操作。外部匹配标识要替换的字符串,内部匹配执行实际替换

代码:

s{ \[ [^\[\]\@]* \@ \K ([^\[\]]*) (?= \] ) }{ $1 =~ tr/a-zA-Z0-9//cdr }xe;
或将每个匹配项替换为
X

s{ \[ [^\[\]\@]* \@ \K ([^\[\]]*) (?= \] ) }{ $1 =~ tr/a-zA-Z0-9/X/cr }xe;
我们匹配前缀
[
,后跟0个或更多非
[
]
@
的字符,后跟
@

\K
用于标记匹配的虚拟开始(即,到目前为止匹配的所有内容都不包括在匹配字符串中,这简化了替换)

我们匹配并捕获0个或更多不是
[
]
的字符

最后,我们在前瞻中匹配后缀
]
(因此它也不是匹配字符串的一部分)

替换部分作为一段代码执行,而不是字符串(如
/e
标志所示)。在这里,我们可以分别使用
$1=~s/[^a-zA-Z0-9]//gr
$1=~s/[^a-zA-Z0-9]/X/gr
,但由于每个内部匹配都只是一个字符,因此也可以使用音译


我们返回修改后的字符串(如
/r
标志所示),并将其用作外部
s
操作中的替换项。

您对
[abc@def“ghi”jkl'123]
?您的理解是正确的,但是这些多个实例中的每一个都是您的全部匹配项。因为您的第一个实例消耗了整个示例字符串……这是在一次迭代后完成的。这就是问题所在。在您的情况下,这比修复更容易描述。它可以通过可变长度的查找来解决,perl不支持这一点支持,或递归,它确实如此。希望有比我更聪明的人能提供一种更直接的方法。@Nick,应该是这样的[abc@defghijkl123]在第一个场景中[abc@defXghiXjklX123]在第二种情况下(当替换为“X”)@zzxyz这是有意义的…即使是对像我这样的非专家…当你写它的时候“描述比修复更容易”这让我觉得至少我的问题不像我想的那么基本:-太好了。我不知道
\K
的存在,它让很多事情变得更容易(一般来说)…总是在学习。只是出于好奇:当你写