当 sed 遇到“无效”字符时,如何强制 sed 继续解析?

当 sed 遇到“无效”字符时,如何强制 sed 继续解析?

我有一个很大的播放列表.pls(用播放软件创建deadbeef),其中还包含一些编码错误的字符串。不过,无论这是一个错误deadbeef还是它的插件之一,我并不真正感兴趣,因为我只想使用sed.

然而,这比乍一看要容易,因为sed会立即打断一旦遇到它认为“无效”的字符就进行解析:

$ cat archives.pls | grep --perl-regexp Lika.*rar.*02
rar:///home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar:2001- �/02 ���.mp3

这是原始线路,未修改。现在让我们尝试sed一下,提取路径(注意:这是一个我即将完成的脚本,用于检查目录树中的“死”音频文件,即那些不再存在的音频文件)

 $ cat archives.pls | grep --perl-regexp Lika.*rar.*02 | sed -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):.*/\1/'
 /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar�/02 ���.mp3

啊。这显然不起作用,尽管正则表达式绝对正确(因为可以证明它可以与所谓的“有效”字符一起正常工作)。实际上rar�来自 Original 的缩写rar:2001- �,表示sed从冒号通过“2001-”解析到空白,然后突然停在无效的 � 字符处。尽管如此,仍然存在一个问题,为什么不sed忽略它是什么编码并无论如何解析呢?我的意思是,我们希望“rar”后面的冒号之后的所有内容都被切断,所以应该可以安全地假设.* 绝对代表任何特点?

注意:请避免cut在答案中使用“看似优雅”的解决方案。去过也做过。相反,您最好考虑一下我们在 UNIX 类型的操作系统上,我们甚至可能合法地使用奇怪的文件名,其中涉及最初拼写为“:wumpscut:”或类似内容的艺术家。换句话说,中间可能还有一个冒号,这就是为什么在 后遇到的第一个冒号静态切断不是一个好主意(zip|rar|7z)://

答案1

正如金发姑娘上面已经评论过的,UTF-8 是多字节可变宽度编码。每个字符最多可由四个字节组成。在无效字节之后,您最多可以希望下一个字节可能会开始一个新字符。

sed尝试匹配字符而不是字节。由于无效字节不是字符,并且是.唯一匹配的字符,因此它不会匹配,并且您的结果绝对正确。

您必须找出该文件使用的编码。 (该file命令可能会帮助您完成此操作。)将环境变量设置LANG为正确的值应该足以按照sed您预期的方式工作。在你的情况下可能LANG=C应该足够了。

答案2

将环境变量设置LANG为某些 UTF-8 编码字符串,例如LANG='en_US.UTF-8'.在这里,在 Mac OS X 10.6.8 上,它LANG='C'导致 GNU sed4.2.1 停止解析,如问题中所述。sed另一方面,GNU 4.2.2 工作得很好。

对我有用的另一个修改(在 Mac OS X 10.6.8 上)是将正则表达式部分替换:.*为::\([^[:print:]]\|[[:print:]]\)*(POSIX 字符类)。

echo $'rar:///home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar:2001- \357\277\275/02 \357\277\275\357\277\275\357\277\275.mp3' > archives.pls

(
export LANG='C'
#locale charmap
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02'
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gsed -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):.*/\1/'    # GNU sed 4.2.1
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gnused -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):.*/\1/'  # GNU sed 4.2.2
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gsed -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):\([^[:print:]]\|[[:print:]]\)*/\1/'
)


# output:
# rar:///home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar:2001- �/02 ���.mp3
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar�/02 ���.mp3
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar

#--------------

(
export LANG='en_US.UTF-8'
#locale charmap
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02'
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gsed -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):.*/\1/'    # GNU sed 4.2.1
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gnused -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):.*/\1/'  # GNU sed 4.2.2
cat archives.pls | grep --perl-regexp 'Lika.*rar.*02' | gsed -e 's/^\(zip\|rar\|7z\):\/\///' -e 's/\(\.\(zip\|rar\|7z\)\):\([^[:print:]]\|[[:print:]]\)*/\1/'
)

# output:
# rar:///home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar:2001- �/02 ���.mp3
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar
# /home/andy/audio_compressed/world/ru/Lika Star - Ya (RUS 2001).rar

相关内容