在这些情况下限制破折号的 RegEx 模式

RegEx pattern to limit dashes in these circumstances

场景

我正在使用一个用 Delphi 编写并支持 pascal 脚本的第三方文件重命名软件:http://www.den4b.com/?x=products&product=renamer

该应用程序允许使用正则表达式来重命名文件。这意味着如果我需要用一个文件名做的事情不能仅使用一个 RegEx 来完成,那么我可以同时使用各种表达式或 pascal 脚本代码来容纳文件名,直到我可以根据需要正确格式化文件名问题或其他...

问题

我需要像下面这样格式化歌曲文件名,在这些文件名中,“...featuring artist”部分位于字符串的右侧,我需要匹配它并将其放在字符串的左侧。

为了便于理解,我们可以想象 tokenize 文件名,如下所示:

[0]ARTIST   [1]DASH   [2]TRACK   [3]FEAT_ARTIST   [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}

然后我需要用 RegEx 做的是格式化文件名以按以下顺序定位 标记

[0]ARTIST   [3]FEAT_ARTIST   [1]DASH   [2]TRACK   [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}

我实际上使用这个正则表达式来做到这一点:

\A([^-]?)\s-\s*(.?)\s([([])?((ft[.\s]|feat[.\s]|featuring[.\s])[^(){}[]]*)([)]])?(.+)?\Z

替换为:

-

问题从这里开始,因为 [0]ARTIST[2]TRACK 标记可能包含破折号,例如这个文件名:

然后,如果我错了,请纠正我,但我认为它不可能以任何方式解决这个问题,因为机器无法预测何时将一个标记与另一个标记分开,什么是名称或什么不是,因为我不知道包含文件名的破折号的数量。

For that reason, instead of looking for ingenuos perfection that could cause bad filenames because the amount of dashes inside, I prefer to look for a filename exclusion solution, by limiting the dashes that the expression should match in the filename.

问题

以我上面显示的 RegEx 为例 extend/improve 它,我如何排除包含 [0]ARTIST 或带破折号的 [2]TRACK 标记的文件名?

...或者换句话说,当文件名包含超过 1 个破折号 before " 时,我如何告诉我的 RegEx 避免修改文件名...特色艺术家”部分? (不在之后)

基本上,Regex 应该确定 [1]DASH 是否在 [3]FEAT_ARTIST 之前被多次找到,如果是,则排除该文件名(不要修改它)

我知道如何限制 Regex 组的出现次数或多或少像这样 ([\-]){1} 以仅匹配 1 个破折号出现,但我不确定如何在表达式中实现它使用。


预期结果

只是一些随机的例子...

只有在 [3]FEAT_ARTIST 之前有一个破折号,这样我们就可以知道何时将 [0]ARTIST[2]TRACK 标记分开。

仅在 [3]FEAT_ARTIST 之前有一个破折号,因此我们可以知道何时将 [0]ARTIST[2]TRACK 标记分开。 [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}.

仅在 [3]FEAT_ARTIST 之前有一个破折号,因此我们可以知道何时将 [0]ARTIST[2]TRACK 标记分开。 [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{} 也包含破折号。

仅在 [0]ARTIST[2]TRACK 标记之间有一个破折号,但文件名没有 [3]FEAT_ARTIST 所以我们不碰它。

仅在 [0]ARTIST[2]TRACK 标记之间有一个破折号,但是 [3]FEAT_ARTIST[1]DASH 之前,所以我们不碰它。

[0]ARTIST 有破折号,所以我们不知道什么时候分开 [0]ARTIST[2]TRACK 标记,所以正则表达式应该排除这个不要修改这个文件名。

[2]TRACK 有破折号,所以我们不知道什么时候分开 [0]ARTIST[2]TRACK 标记,所以正则表达式应该排除这个不要修改这个文件名。

[0]ARTIST[2]TRACK 标记有破折号,所以我们不知道什么时候把它们分开,所以正则表达式应该排除这个不要修改这个文件名。

[0]ARTIST[2]TRACK 标记有破折号,而且 [3]FEAT_ARTIST 不存在,这里也无事可做。

我希望这有助于了解我的需求。

我认为您唯一需要 realize/change 的是 "separator hyphen" 和 "embedded hyphens" 之间有明显的区别。即 none 的嵌入式连字符在两边都有空格(我预计;您需要验证这一点)。您需要做的就是将上面的正则表达式的开头从 \A([^-]?)\s-\s* 更改为 \A(.?)\s-\s+...

我把你所有的文件名都输入到文本编辑器 UltraEdit 22.10 版中:

Carbin & Sirmark - Sorry Feat. Sevener
Kristjan Cash Cash - Take Me Home Feat. Bebe Rexha (Revoke Remix)
Dj E-nergy C-21 - My Super-hero track! feat Dj Ass-hole
Flight Facilities - Heart Attack Feat. Owl Eyes (Snakehips Remix)
Flight Facilities - Heart Attack Feat. Owl Eyes [Snake--hips Remix]
Fedde Le Grand - Cinematic
Fedde Le Grand Feat. Denny White - Cinematic
Artist-Name - Track Name feat someone
Artist Name - Track-Name feat someone
Dj E-nergy C-21 - My Super-hero track! feat Dj Ass-hole
Dj E-nergy C-21 - My Super-hero track!

用Perl正则表达式搜索字符串

^(.+) - (.+?) ((?:featuring|feat\.?|ft\.?) +(?:[^\r\n (\[{]| (?![(\[{]))+)

和替换字符串

  - 

这些文件名被修改为不区分大小写全部替换为

Carbin & Sirmark Feat. Sevener - Sorry
Kristjan Cash Cash Feat. Bebe Rexha - Take Me Home (Revoke Remix)
Dj E-nergy C-21 feat Dj Ass-hole - My Super-hero track!
Flight Facilities Feat. Owl Eyes - Heart Attack (Snakehips Remix)
Flight Facilities Feat. Owl Eyes - Heart Attack [Snake--hips Remix]
Fedde Le Grand - Cinematic
Fedde Le Grand Feat. Denny White - Cinematic
Artist-Name feat someone - Track Name
Artist Name feat someone - Track-Name
Dj E-nergy C-21 feat Dj Ass-hole - My Super-hero track!
Dj E-nergy C-21 - My Super-hero track!

这看起来就是你想要的。 UltraEdit 使用 Boost Perl 正则表达式库。

如果文件重命名工具还支持否定先行和贪婪匹配行为,则可能对此任务有用的表达式是:

\A(.+) - (.+?) ((?:featuring|feat\.?|ft\.?) +(?:[^ (\[{]| (?![(\[{]))+)

替换字符串也是:

  - 

搜索字符串的解释:

^ ...行首
\A ... 缓冲区开始

(.+) -  ... 一个 greedy 表达式匹配任何字符 1 次或多次(换行符除外)直到 last 在标记组中出现 space破折号space 不包括  - ,这仍然会导致 整个表达式 .

的正匹配

(.+?)  ... 非贪婪 表达式也在捕获组中匹配任何字符(换行符除外)一次或多次直到下一次出现space 和...

(?:featuring|feat\.?|ft\.?) + ... 单词 featuring OR 缩写 feat 带或不带点 OR 缩写 ft 带或不带点 AND 1 个或多个 spaces.

( ... 第三个捕获组的开始。

(?:[^\r\n (\[{]| (?![(\[{]))+ ...一个非标记组匹配

  • 一个字符不是
    • 回车符 return 或换行符(仅限 UE 搜索字符串),或
    • 左括号,或
    • 左方括号,或
    • 左大括号

  • a space 使用否定先行表达式检查下一个字符是否 not
    • 左括号,或
    • 左方括号,或
    • 左大括号

一次或多次。换句话说,最后一个表达式匹配文件名结尾或 ([{ 之前的所有内容,不包括留给这些字符的 space 以避免得到 spacespacedash after FEAT_ARTIST after replace.

) ...终于结束了第三个捕获组。


编辑 1: 同样有效(在 UltraEdit 中)的是搜索字符串:

^(.+) - (.+?) ((?:featuring|feat|ft)[ .]+(?:[^\r\n (\[{]| (?![(\[{]))+)

除了 featuring. 之外,还可以使表达更容易一些。


编辑 2: 同样有效(在 UltraEdit 中)的是搜索字符串:

^((?:.(?! - ))+.) - ((?:.(?! - ))+) ((?:featuring|feat|ft)[ .]+(?:[^\r\n (\[{]| (?![(\[{]))+)

忽略所有包含两个 spacedashspace[=129 的行=] 留给 FEAT_ARTIST.

如果当前字符之后的字符串不是 space破折号[,则此表达式使用否定先行逐字符匹配=92=]space。这允许用于第一个捕获组,它选择第一个 spacedash[ 左边的最后一个字符的字符串=167=],但对于第二个捕获组,不应再有 spacedashspace 因为这肯定会导致整个表达式的结果为负。

试试:

^(.+)\s+-\s+(.+?)\s+[fF](t|eat(uring)?)?\.?([^([\])\n]+)(.+)?$

DEMO

并使用替换为: Feat. -

我用 ReNamer 和 Regex101 试过了,如果有 - (</code> + <code>- + </code>) in artist name, like <code>artist - name, 但是如果标题部分有这样的片段,它会失败。

^(.+)\s+-\s+ 部分在序列 space-dash-space 之前使用贪婪量词 .+,它被视为艺术家姓名和曲目标题之间的分隔符。所以它会尽可能多地匹配,直到最后一次出现 -,因此,它会 "ignore" 艺术家姓名中带有空格的破折号,但如果这样的话,它会出现无效匹配元素出现在曲目标题中。所以:

  • Artist - name - track title feat. someone - 它将被匹配并且 修改妥当,
  • Artist name - track - title feat. someone - 它会失败,如文本 将在最后一个破折号处拆分。

我使用 [fF](t|eat(uring)?)?\.? 代替 (ft[.\s]|feat[.\s]|featuring[.\s]) 匹配相似,但应该工作得更快(它应该稍微限制回溯)。

在我的演示中,有一个 + 而不是 \s+ (如上),因为它会在演示中匹配多行,并显示无效结果,但在单行情况下,就像你的问题, 它应该可以正常工作。

在@m.cekiera的正则表达式的帮助下,我通过使用 pascal 脚本解决了这个问题,当在文件名:

// Formats an audio filename that has the "...featuring artist" part at the end of filename.
//------------------------------------------------------------------------------------------


// Pseudo-Example:
//
// From: [0]ARTIST_NAME  [1]DASH  [2]TRACK_TITLE  [3]FEAT_ARTIST  [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}
// To:   [0]ARTIST_NAME  [3]FEAT_ARTIST  [1]DASH  [2]TRACK_TITLE  [4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}

// Real-Example:
//
// From: Carbin & Sirmark - Sorry Feat. Sevener.mp3
// To:   Carbin & Sirmark Feat. Sevener - Sorry.mp3

// Known limitations:
//
// • If [0]ARTIST_NAME or [2]TRACK_TITLE parts contains any " - " the script will not work properlly.
//   By default the script prevents any replacement on that kind of filenames, so don't worry.


var
  rgxPattern: string;
  rgxReplace: string;
  dashCount: integer;
  baseName: string;
  extension: WideString;

begin

  baseName  := WideExtractBaseName(FileName)
  extension := WideExtractFileExt(FileName);

  // The regular expression that matches the filename parts.
  // 
  rgxPattern := '^(.+)\s+-\s+(.+?)\s+[fF](t|eat(uring)?)?\.?([^([\])\n]+)(.+)?$'
  rgxReplace := ' Feat. - '

  // The amount of " - " that contains the filename.
  dashCount := high(MatchesRegEx(baseName, '\s-\s' , false));

  // If only one " - " is found then...
  If (dashCount = 0) Then
    begin // Do the replacement.
      baseName := ReplaceRegEx(baseName, rgxPattern, rgxReplace, false, true)
      FileName := baseName + extension;
    end;

end.