我需要在具有特定编码(通常是 utf8)的文件中搜索字符串(字符序列),但返回结果的字符偏移量(而不是字节偏移量)。
所以这是一个应该进行的搜索独立的字符串/文件的编码。
grep
显然不能做到这一点,那么我应该使用哪个工具呢?
示例(正确):
$ export LANG="en_US.UTF-8"
$ echo 'aöæaæaæa' | tool -utf8 'æa'
2
4
6
示例(错误):
$ export LANG="en_US.UTF-8"
$ echo 'aöæaæaæa' | tool 'æa'
3
6
9
答案1
在当前版本的 Perl 中,您可以使用@-
和@+
魔法数组来获取整个正则表达式和任何可能的捕获组的匹配位置。两个数组的第 0 个元素保存与整个子字符串相关的索引,这也是$-[0]
您感兴趣的元素。
作为单行:
$ echo 'aöæaæaæa' | perl -CSDLA -ne 'BEGIN { $pattern = shift }; printf "%d\n", $-[0] while $_ =~ m/$pattern/g;' æa
2
4
6
或者完整的脚本:
#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Encode;
use open ":encoding(utf8)";
undef $/;
my $pattern = decode_utf8(shift);
binmode STDIN, ":utf8";
while (<STDIN>) {
printf "%d\n", $-[0] while $_ =~ m/$pattern/g;
}
例如
$ echo 'aöæaæaæa' | perl match.pl æa -
2
4
6
(后一个脚本仅适用于标准输入。我似乎无法强制 Perl 将所有文件视为 UTF-8。)
答案2
和zsh
:
set -o extendedglob # for (#m) which in patterns causes the matched portion to be
# made available in $MATCH and the offset (1-based) in $MBEGIN
# (and causes the expansion of the replacement in
# ${var//pattern/replacement} to be deferred to the
# time of replacement)
haystack=aöæaæaæa
needle=æ
offsets=() i=0
: ${haystack//(#m)$needle/$((offsets[++i] = MBEGIN - 1))}
print -l $offsets
答案3
使用 GNUawk
或任何其他 POSIX 兼容awk
实现(不是mawk
),以及正确的语言环境设置:
$ LANG='en_US.UTF-8' gawk -v pat='æa' -- '
{
s = $0;
pos = 0;
while (match(s, pat)) {
pos += RSTART-1;
print "file", FILENAME ": line", FNR, "position", pos, "matched", substr(s, RSTART, RLENGTH);
pos += RLENGTH;
s = substr(s, RSTART+RLENGTH);
}
}
' <<<'aöæaæaæa'
file -: line 1 position 2 matched æa
file -: line 1 position 4 matched æa
file -: line 1 position 6 matched æa
$
-v pat
参数中指示的模式gawk
可以是任何有效的正则表达式。