regex - 替换所有出现的字符,除非被两个不同的模式包围

标签 regex perl

我想找到一个正则表达式(最好是在 Perl 中,但任何风格都可以)来替换每个 _,除了那些前面有 8 位数字 后面有 6 位数字的代码。
实际上,我想替换文件名中的 _ ,除了格式为 YYYYMMDD_hhmmss 的日期中的文件名。
一般来说,我想替换前面没有某种模式并且后面没有其他模式出现的某个字符。
我尝试了很多正则表达式并在网上查找了很多,但我没有找到任何东西!

我知道可以用 _ 替换每个 . ,然后恢复 _ 中的 YYYYMMDD.hhmmss ,但我有兴趣一步完成(希望这是可能的)。

以下是一些替换示例:

Patate_17890505_TitreEnCamelCase.ext  -->  Patate.17890505.TitreEnCamelCase.ext
EPFL_AlgebreLineaire                  -->  EPFL.AlgebreLineaire
ipe.20210302_005606.pdf               -->  ipe.20210302_005606.pdf
1_                                    -->  1.
12_                                   -->  12.
_1                                    -->  .1
_12                                   -->  .12
12345678_                             -->  12345678.
_123456                               -->  .123456
12345678_12345                        -->  12345678.12345
1234567_123456                        -->  1234567.123456
1234567_12345                         -->  1234567.12345
123456_12345                          -->  123456.12345
12345678_1234567                      -->  12345678.1234567
123456789_123456                      -->  123456789.123456
123456789_1234567                     -->  123456789.1234567
_patate__truc__                       -->  .patate..truc..
___                                   -->  ...
foo_12345678                          -->  foo.12345678
foo_12345678_123456_bar               -->  foo.12345678_123456.bar
12345678_123456                       -->  12345678_123456
foo12345678_123456bar                 -->  foo12345678_123456bar

下面是我尝试过的一些示例。


与我想要的完全相反,替换每个 _ 前面正好是 8 位数字,后面正好是 6 位数字(在 regex101 上尝试一下):

s/((?<!\d)(?:\d{8}))_((?:\d{6})(?!\d))/$1.$2/g

它有效,所以我需要这个正则表达式的否定......


只是负向回顾和负向超前(在 regex101 上尝试一下):

s/(?<!\d{8})_(?!\d{6})/./g

失败:如果 _ 前面正好有 8 位数字后面正好有 6 位数字,则不会替换,例如这些字符串中的 _ 不会被替换:

12345678_
_123456
12345678_12345
1234567_123456

我需要替换除“and”之外的所有内容,但是这个替换除“or”之外的所有内容(因此它错过了一些 _ )。


灵感来自 this answer (来自 python regex: match a char surrounded by exactly 2 chars )(在 regex101 上尝试一下):

s/(?<!(?<!\d)\d{8})_(?!\d{6}(?!\d))/./g

失败:原因与上一个相同。
原始答案中的正则表达式有效,因为它替换了前面是前模式 后面是后模式的字符。


受到 this answer (来自 Replace character UNLESS surrounded by specific tag )的启发,但我不太明白它是如何工作的(在 regex101 上尝试一下):

s/_(?:(?!(?:.*?\d{6}))|(?=[^\d]+\d{8}))/./g

失败:在这些示例中,_ 未被替换

_123456
1234567_123456
12345678_1234567
123456789_123456
123456789_1234567
foo_12345678

原来的问题与我的非常接近,但是前模式和后模式不是 \d{8}\d{6} ,而是 HTML 标记,因此问题更容易: <tag></tag> 是独特的元素,对于我的问题,后-pattern \d{6} 后面可以跟一个其他数字(同样,预模式 \d{8} 前面可以跟一个其他数字)。
但这几乎可以工作,与之前的尝试不同,它替换了这两个字符串中的_:

12345678_
12345678_12345

所以也许修改可以让它按照我想要的方式工作......

最佳答案

你可以使用

(?<!\d)\d{8}_\d{6}(?!\d)(*SKIP)(*F)|_

请参阅regex demo详细信息:

  • (?<!\d)\d{8}_\d{6}(?!\d) - 八位数字,_以及未包含任何其他数字的六位数字
  • (*SKIP)(*F) - 在当前位置匹配失败,并从失败位置继续正则表达式搜索
  • | - 或
  • _ - 任何其他上下文中的下划线。

另一种正则表达式是

_(?!(?<=(?<!\d)\d{8}_)\d{6}(?!\d))

参见this regex demo详细信息:

  • _ - 下划线
  • (?!(?<=(?<!\d)\d{8}_)\d{6}(?!\d)) - 如果出现以下情况,则表示匹配失败的负向前瞻 - 紧邻当前位置的右侧 - 紧接其后有六位(且不超过六位)数字,且前面正好有八位数字和下划线。

关于regex - 替换所有出现的字符,除非被两个不同的模式包围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71848318/

相关文章:

php - 将字符串拆分为带有瑞典字符的单词

regex - 如何使用正则表达式仅匹配每行字符串的第一次出现

perl - 是什么导致字符串 `eval` 、代码属性和符号表之间发生意外交互?

mysql根据条件添加时间

钩子(Hook)后的 Perl 舞者

java - 正则表达式 for/someChars1@someChars2BOT

regex - 使用正则表达式匹配两个字符串之间的字符串

javascript - 在字符串数组中查找数字

javascript - 运行脚本后如何返回成功

perl - 如何从大型文本文件中删除停用词?