如何lcfirst每组词?

How to lcfirst each group of words?

为什么这没有像我预期的那样工作?

$ echo 'JOHN, BO PEEP, BoPeep' | perl -0777ne 'map { print ($_) if $_ ne uc($_) } split /,/'
 BoPeep

$ echo 'JOHN, BO PEEP, BoPeep' | perl -0777ne 'map { print lcfirst($_) if $_ ne uc($_) } split /,/'
 BoPeep

这两个命令之间的唯一区别是 print ($_)print lcfirst($_),而我原以为 print lcfirst($_) 会输出 boPeep

我错过了什么?

示例输入:

JOHN, BO PEEP, BoPeep, AVG, Hex_String_Literal, Time_String, MODULE

问题是单独使用逗号拆分会在每个术语中留下前导空格。

解决这个问题的一种方法是拆分感兴趣的标记之间的所有内容,这里 /\s*,\s*/(回想一下 split 那里采用了完整的合法正则表达式)。进行其他简化

echo "JOHN, BO PEEP, BoPeep, AVG, Hex_String_Literal, Time_String, MODULE"
    | perl -nlE'$_ ne uc($_) and say lcfirst($_) for split /\s*,\s*/'

这会在单独的一行上打印单词 boPeephex_String_Literaltime_String。要将它们清晰地打印在由空格分隔的一行中,我们需要更多一些。例如

perl -nE'say join " ", map { $_ ne uc($_) ? lcfirst($_) : () } split /\s*,\s*/'

这使用了一个技巧来通过 map 进行过滤:return 一个空列表 () 用于我们要过滤掉的内容(all-caps 个单词),它被展平在整个输出列表中什么都没有。或者,过滤然后进一步处理

perl -nE'say join " ", map { lcfirst($_) } grep { $_ ne uc($_) } split /\s*,\s*/'

这首先处理从 splitgrep 的列表,然后用 map 遍历剩余项目的输出列表。这可能比一次遍历整个列表的三元组效率低一些,但这肯定不会在任何合理的工作负载中显示出来。

一些评论

  • 我已经删除了 -0777,因为我不确定它的实用性——如果它在文件上运行并且需要处理文本中的多行块,那么我们需要一些其他规定无论如何。如果它变成 line-by-line 那么就没有必要了。如果需要,把它放回去 :)

  • 那种提供 say(以及 所有 其他功能!)的便捷 -E switch 在以下人眼中并不好未来的兼容性。如果这是或可能成为一个问题,那么使用 CORE::say,或者当然是 print "...", "\n",并保留 -e 而不是 -E