我有一个很大的播放列表.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 sed
4.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