【问题标题】:How to change partial substrings to mixed case in Perl如何在 Perl 中将部分子字符串更改为混合大小写
【发布时间】:2012-06-15 21:03:37
【问题描述】:

Perl 重命名实用程序在日常文件工作中做得很好,尤其是在我不时检查我的 MP3 收藏时。

有些人有一个可怕的习惯,总是把艺术家的名字大写,就像这样

03 - ARTIST NAME - Song Title.mp3

我希望将它重命名为看起来更平静的混合大小写,像这样

03 - Artist Name - Song Title.mp3

但是,它也应该以一种智能的方式完成。

可能有法语或意大利语标题,例如:

05 - JEAN BAGUETTE - Honi soit qui mal y pense.mp3

07 - SIGNORE AL FORNO - Pazzi sono tutti i calciatori.mp3

因此,我确实希望这些在- 之后更改为混合大小写,只有艺术家部分。

一种行不通的方法是

rename 's/(\d{1,2} - )([A-Z ]+)([\s\S]+)/$1lc($2)$3/' filename

无论如何我都会发布它,因为它总能让你更好地了解我想要做什么。

我还尝试用临时变量替换$2(因为我不允许更改$2,因为它是只读的)并在打印结果之前执行lc(),但我没有成功。但无论如何,我是一个 Perl 初学者。

【问题讨论】:

  • 所以您想将第一对连字符之间的所有内容更改为大小写混合?

标签: perl rename partial utility lowercase


【解决方案1】:

您可以在您的正则表达式中使用 ucfirst (\u) 和 lc (\L) 的转义序列,并结合需要进一步向前冲刺的前瞻断言:

perl -pe 's/(\w)(\w+)(?=.*-)/\u$1\L$2/g'

如果要更正确,可以使用\pL(字母)代替\w。我认为这将适用于 rename 工具。

预计到达时间:已更新为使用 \u 而不是 \U

【讨论】:

  • 你想要\u 作为第一个,而不是\U
  • @tchrist 实际上,因为$1 只是一个字母,所以这并不重要。但我想它可以简化为s/(\w+)(?=.*-)/\u\L$1/g
  • 不,这不是真的。对于所有可能的代码点,大写和标题大写不是相同的 casemap。同样,首先映射到 lc 然后再映射到 tc 或 uc 实际上在形式上是不正确的。你必须写\u$1\L$2,而不是\u\L$2。是的,只有六个代码点很重要:U+0130、U+03F4、U+1E9E、U+2126、U+212A 和 U+212B。
  • 您刚刚为此警告获得了额外的 +1。这真是一个恶魔般的陷阱,很容易让初学者陷入困境。
【解决方案2】:

我知道的最明确的方法是进行两阶段评估(使用/e 修饰符)替换。

在下面的代码中,外部替换选择艺术家姓名,包括其边界连字符并替换它。

内层采用$1 - 艺术家姓名 - 并将非空格字符的每个子序列替换为相同的字符串,首先小写为lc,然后大写为ucfirst

就目前而言,程序将打印之前和之后的名称。从重命名行中删除 # 以进行重命名。

use strict;
use warnings;

my @data = (
  '05 - JEAN BAGUETTE - Honi soit qui mal y pense.mp3',
  '07 - SIGNORE AL FORNO - Pazzi sono tutti i calciatori.mp3',
);

for my $file (@data) {

  (my $new = $file) =~ s{(-[^-]+-)}{
    (my $artist = $1) =~ s/(\S+)/ucfirst lc $1/eg;
    $artist;
  }e;

  print "$file\n";
  print "$new\n";
  print "\n";
  # rename $file, $new;
}

输出

05 - JEAN BAGUETTE - Honi soit qui mal y pense.mp3
05 - Jean Baguette - Honi soit qui mal y pense.mp3

07 - SIGNORE AL FORNO - Pazzi sono tutti i calciatori.mp3
07 - Signore Al Forno - Pazzi sono tutti i calciatori.mp3

更新

您可能喜欢的另一种方法是在连字符上拆分文件名,编辑第二部分并将它们重新组合在一起。

上面的主循环变成了

for my $file (@data) {

  my @file = split /-/, $file;
  $file[1] =~ s/(\S+)/ucfirst lc $1/eg;
  my $new = join '-', @file;

  print "$file\n";
  print "$new\n";
  print "\n";
  # rename $file, $new;
}

功能和输出不变。


更新 2

我刚刚尝试使用 /-.*?-/ 然后使用 substr($_, $-[0], $+[0]) 作为 =~ s/// 的左值,但遗憾的是它不起作用

这似乎是个好主意,我不得不尝试一下。

您对substr 的调用是错误的,因为$-[0]$+[0] 是字符串的偏移量。 substr的第三个参数必须是字符串length所以需要写substr($_, $-[0], $+[0] - $-[0])

这段代码运行良好,并且再次产生与之前相同的结果

for my $file (@data) {

  next unless $file =~ /-[^-]+-/;
  my $new = $file;
  substr($new, $-[0], $+[0]-$-[0]) =~ s/(\S+)/ucfirst lc $1/eg;

  print "$file\n";
  print "$new\n";
  print "\n";
  # rename $file, $new;
}

【讨论】:

  • 是的,这可能是最好的方法。我刚刚尝试使用/-.*?-/,然后使用substr($_, $-[0], $+[0]) 作为=~ s/// 的左值,但遗憾的是它没有用。
  • @ephemient:谢谢你的好主意!我再次更新了我的答案,它工作正常。 Perl 的新用法。
  • 当我们在右侧有\u\L 时,不需要做/e
  • 非常感谢您的努力!好吧,如果我需要一个完整的类似程序的脚本,我会自己写一些,但我觉得这必须以一种更简洁的方式可行。就像上面的一样。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-20
  • 2011-10-06
  • 2021-03-30
  • 2014-02-04
  • 2012-03-04
  • 2016-01-22
  • 1970-01-01
相关资源
最近更新 更多