【问题标题】:RegEx pattern to limit dashes in these circumstances在这些情况下限制破折号的正则表达式模式
【发布时间】:2015-09-27 12:04:53
【问题描述】:

场景

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

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

问题

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

  • Carbin 和 Sirmark - 对不起壮举。七人
  • Kristjan Cash Cash - 带我回家壮举。 Bebe Rexha(撤销混音)

为了让这个简单易懂,我们可以想象 tokenize 像这样的文件名:

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

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

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

我实际上是使用这个 RegEx 来做到这一点的:

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

替换为:

$1 $4 - $2$7

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

  • Dj E-nergy C-21 - 我的超级英雄曲目!壮举 Dj 混蛋

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

因此,与其寻找可能导致坏事的巧妙完美 文件名因为里面的破折号数量,我更喜欢找一个 文件名排除解决方案,通过限制表达式的破折号 应该在文件名中匹配。

问题

以我上面展示的 RegEx 为例来扩展/改进它,我如何排除包含[0]ARTIST 或带有破折号的[2]TRACK 标记的文件名?

...或者换句话说,当文件名包含超过 1 个破折号...特色艺术家强>”部分? (不是之后)

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

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


预期结果

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

仅在 [3]FEAT_ARTIST 之前添加一个破折号,以便我们知道何时将 [0]ARTIST[2]TRACK 标记分开。

  • 来自:'Carbin & Sirmark - 抱歉的壮举。七人'
  • 致:'Carbin & Sirmark Feat。 Sevener - 对不起'

只在[3]FEAT_ARTIST 之前添加一个破折号,这样我们就可以知道何时将[0]ARTIST[2]TRACK 标记分开。与[4]POSSIBLE_ADDITIONAL_INFO_INSIDE:()[]{}

  • 来自:'飞行设施 - 心脏病发作壮举。猫头鹰的眼睛(蛇臀混音)'
  • 致:'飞行设施壮举。猫头鹰的眼睛 - 心脏病发作(Snakehips Remix)'

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

  • 来自:'飞行设施 - 心脏病发作壮举。 Owl Eyes [Snake--hips Remix]'
  • 致:'飞行设施壮举。猫头鹰的眼睛 - 心脏病发作 [Snake--hips Remix]'

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

  • 来自:'Fedde Le Grand - 电影'
  • 致:'Fedde Le Grand - 电影'

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

  • 来自:'Fedde Le Grand Feat。 Denny White - 电影'
  • 致:'Fedde Le Grand Feat。 Denny White - 电影'

[0]ARTIST 有破折号,因此我们不知道何时将[0]ARTIST[2]TRACK 标记分开,因此正则表达式应排除它以不修改此文件名。

  • 来自:'Artist-Name - Track Name feat someone'
  • 致:'Artist-Name - Track Name feat someone'

[2]TRACK 有破折号,所以我们不知道何时将[0]ARTIST[2]TRACK 标记分开,因此正则表达式应排除它以不修改此文件名。

  • 发件人:'艺术家姓名 - 曲目-名字的壮举'
  • 致:“艺人姓名 - 曲目名称,壮举某人
  • 来自:'Dj E-nergy C-21 - 我的超级英雄曲目!壮举 Dj 混蛋'
  • 致:'Dj E-nergy C-21 - 我的超级英雄曲目!壮举 Dj 混蛋'

[0]ARTIST[2]TRACK 标记有破折号,[3]FEAT_ARTIST 也不存在,这里也没什么可做的。

  • 来自:'Dj E-nergy C-21 - 我的超级英雄曲目!'
  • 致:'Dj E-nergy C-21 - 我的超级英雄曲目!'

我希望这有助于理解我需要什么。

【问题讨论】:

  • 只是一个想法,但字符串是否可以包含分隔字符串各个部分的不可打印字符(如 )?这些可以在打印到屏幕之前删除或由正则表达式解析器使用。
  • @AlainD 感谢您的评论,文件名不包含控制字符,最初在某些情况下我发现文件名包含特殊的 unicode 破折号或其他特殊符号,但我之前删除/转换了所有这些特殊字符使用这个问题的正则表达式。
  • 什么是正则表达式风格?如果您不指定,那么得到不使用相同风格的答案也就不足为奇了。

标签: regex delphi pattern-matching filenames pascalscript


【解决方案1】:

尝试:

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

DEMO

并使用替换为:$1 Feat.$5 - $2$6

我用 ReNamer 和 Regex101 进行了尝试,如果艺术家名称中有 - ( + - + ),例如 artist - name,它也可以工作,但如果有,它将失败标题部分中的此类片段。

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

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

我使用了[fF](t|eat(uring)?)?\.? 而不是(ft[.\s]|feat[.\s]|featuring[.\s]),它匹配相似,但应该更快(它应该抑制回溯)。

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

【讨论】:

  • 谢谢您,但是您使用什么语法来构建该表达式?我认为 Delphi(或至少我正在使用的应用程序)不支持这个:(?:...),我无法使用它。
  • 然后尝试使用(.+?)\s-\s*(.+?)[fF]eat(uring)?.([^([\])\n]+)(.+)? 并使用\1 Feat. \4 - \2 \5 补充demo
  • 似乎更接近,但它在我上面给出的一些示例中失败了,它用Artist Feat. Someone - Name-Track Name.txt 修改Artist-Name - Track Name feat someone.txt 或者用Dj E-nergy C-21 - My Super-hero track! feat Dj Ass-hole.txt 修改Dj E Feat. Dj Ass-Hole-Nergy C-21 - My Super-Hero Track!.txt 在这种情况下,正则表达式不应该' t 修改原始字符串。
  • 您在评论中显示的正则表达式非常接近,它只需要在“...feat”部分之前发现多个“ - ”时避免修改文件名。这个正则表达式解决了特定问题(.+?)(\s-\s*){1}(.+?)[fF]eat(uring)?.([^([\])\n]+)(.+)? 与替换$1 $2 $3 Feat. $5 但它只适用于这样的文件名:Dj E-nergy C - 21 - My Super-hero track! feat Dj Ass-hole,没有其他,因为我所做的替换顺序......所以我的正则表达式完全是错误的。
  • @ElektroStudios 我更新了答案,我在 ReNamer 中对其进行了测试,我认为它工作正常
【解决方案2】:

我认为您唯一需要意识到/改变的是“分隔符连字符”和“嵌入式连字符”之间存在明显差异。也就是说,嵌入的连字符都不会在两边都有空格(我希望;您需要验证这一点)。您需要做的就是将上面的正则表达式的开头从 \A([^-]?)\s-\s* 更改为 \A(.?)\s-\s+...

【讨论】:

  • 感谢您的回答,但您建议的修改对任何示例都不起作用!你知道为什么吗?。
  • 您的问题中显示的正则表达式可能存在其他问题(错误?)。没有看到更多细节,我无法说出原因。试试“\A(.*)\s-\s+”...
【解决方案3】:

我将你所有的文件名放入文本编辑器 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 (\[{]| (?![(\[{]))+)

和替换字符串

$1 $3 - $2

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

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\.?) +(?:[^ (\[{]| (?![(\[{]))+)

替换字符串也是:

$1 $3 - $2

搜索字符串说明:

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

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

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

(?:featuring|feat\.?|ft\.?) + ... word featuring 或缩写 feat 带或不带点 OR 缩写 ft 带或不带点 AND 1 个或多个空格。

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

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

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

  • 使用负前瞻表达式检查下一个字符 不是 是否存在的空格
    • 左括号,或
    • 左方括号,或
    • 开口大括号

一次或多次。换句话说,最后一个表达式匹配到文件名结尾或([{ 的所有内容,不包括留给这些字符的空格以避免获得spacespace破折号FEAT_ARTIST之后替换。

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


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

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

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


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

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

忽略所有包含两个 spacedashspaceFEAT_ARTIST 的行。

如果当前字符后的字符串不是 spacedashspace,则此表达式使用否定的前瞻来逐个字符匹配。这允许第一个捕获组选择字符串直到第一个 spacedashspace 左侧的最后一个字符,但对于第二个捕获组应该不再是 spacedashspace 因为这肯定会导致整个表达式的否定结果。

【讨论】:

  • 感谢您的回答,但这些正则表达式似乎不受应用程序的 RegEx 电机支持(我不确定作者是否为此使用了 Delphi/Object-pascal 或 Pascal-script)。这会在位置 41 ^(.+) - (.+?) ((?:featuring|feat\.?|ft\.?) +(?:[^\r\n (\[{]| (?![(\[{]))+) 和位置 42 \A(.+) - (.+?) ((?:featuring|feat\.?|ft\.?) +(?:[^ (\[{]| (?![(\[{]))+) 抛出“未知修饰符”的错误无论如何我会阅读并调查您的解释以找到某种等价物(如果我幸运的)。再次感谢!
  • 我完全不明白你是如何将这个Dj E-nergy C-21 - My Super-hero track! feat Dj Ass-hole 替换为这个else Dj E-nergy C-21 feat Dj Ass-hole - My Super-hero track!,你的表达式如何知道在很多破折号时插入...feat 部分的确切位置在字符串中???你已经解释了这个逻辑?,真的很好!我想找到一个像你展示的那样的“兼容”解决方案。
  • 只要 ARTISTTRACK 部分不包含连字符或破折号,左侧有空格,右侧有空格,搜索表达式就可以工作,因为这用于分隔字符串.
  • 明白了,但是ARTISTTRACk 也可以有一个带空格的破折号,那么我真正需要的是一个仅限搜索一个“-”的搜索表达式>,如果在FEAT_ARTIST 之前找到两个 " - " 则不要修改字符串。无论如何,不​​幸的是,该应用程序似乎不接受我所说的您使用的语法。我会继续努力的。谢谢。
  • 我又添加了一个搜索字符串,它会忽略带有多个“ - ”的文件名留给特色艺术家。但这仅在重命名工具支持负前瞻时才有效。如果您的重命名工具不支持,请将所有文件名转换为文本文件并使用 UltraEdit 将文本文件转换为批处理文件,并使用 ren 命令更改这些文件名并使用 rem 保留这些文件名的命令。
【解决方案4】:

在@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.
  // http://stackoverflow.com/questions/32807698/regex-pattern-to-limit-dashes-in-these-circumstances
  rgxPattern := '^(.+)\s+-\s+(.+?)\s+[fF](t|eat(uring)?)?\.?([^([\])\n]+)(.+)?$'
  rgxReplace := '$1 Feat.$5 - $2$6'

  // 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.   

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多