可能的最终解决方案
因此,我采用了以下所有信息并提出了以下建议:
for class in $(
locale -v LC_CTYPE |
sed 's/combin.*//;s/;/\n/g;q'
) ; do
printf "\n\t%s\n\n" $class
recode u2/test16 -q </dev/null |
tr -dc "[:$class:]" |
od -A n -t a -t o1z -w12
done
注意:
我将od
以上用作首选的最终过滤器,因为我知道我不会使用多字节字符,因为它无法正确处理。 recode u2..dump
都将生成更类似于问题中指定的输出,并正确处理宽字符。
输出值
upper
A B C D E F G H I J K L
101 102 103 104 105 106 107 110 111 112 113 114 >ABCDEFGHIJKL<
M N O P Q R S T U V W X
115 116 117 120 121 122 123 124 125 126 127 130 >MNOPQRSTUVWX<
Y Z
131 132 >YZ<
lower
a b c d e f g h i j k l
141 142 143 144 145 146 147 150 151 152 153 154 >abcdefghijkl<
m n o p q r s t u v w x
155 156 157 160 161 162 163 164 165 166 167 170 >mnopqrstuvwx<
y z
171 172 >yz<
alpha
A B C D E F G H I J K L
101 102 103 104 105 106 107 110 111 112 113 114 >ABCDEFGHIJKL<
M N O P Q R S T U V W X
115 116 117 120 121 122 123 124 125 126 127 130 >MNOPQRSTUVWX<
Y Z a b c d e f g h i j
131 132 141 142 143 144 145 146 147 150 151 152 >YZabcdefghij<
k l m n o p q r s t u v
153 154 155 156 157 160 161 162 163 164 165 166 >klmnopqrstuv<
w x y z
167 170 171 172 >wxyz<
digit
0 1 2 3 4 5 6 7 8 9
060 061 062 063 064 065 066 067 070 071 >0123456789<
xdigit
0 1 2 3 4 5 6 7 8 9 A B
060 061 062 063 064 065 066 067 070 071 101 102 >0123456789AB<
C D E F a b c d e f
103 104 105 106 141 142 143 144 145 146 >CDEFabcdef<
space
ht nl vt ff cr sp
011 012 013 014 015 040 >..... <
print
sp ! " # $ % & ' ( ) * +
040 041 042 043 044 045 046 047 050 051 052 053 > !"#$%&'()*+<
, - . / 0 1 2 3 4 5 6 7
054 055 056 057 060 061 062 063 064 065 066 067 >,-./01234567<
8 9 : ; < = > ? @ A B C
070 071 072 073 074 075 076 077 100 101 102 103 >89:;<=>?@ABC<
D E F G H I J K L M N O
104 105 106 107 110 111 112 113 114 115 116 117 >DEFGHIJKLMNO<
P Q R S T U V W X Y Z [
120 121 122 123 124 125 126 127 130 131 132 133 >PQRSTUVWXYZ[<
\ ] ^ _ ` a b c d e f g
134 135 136 137 140 141 142 143 144 145 146 147 >\]^_`abcdefg<
h i j k l m n o p q r s
150 151 152 153 154 155 156 157 160 161 162 163 >hijklmnopqrs<
t u v w x y z { | } ~
164 165 166 167 170 171 172 173 174 175 176 >tuvwxyz{|}~<
graph
! " # $ % & ' ( ) * + ,
041 042 043 044 045 046 047 050 051 052 053 054 >!"#$%&'()*+,<
- . / 0 1 2 3 4 5 6 7 8
055 056 057 060 061 062 063 064 065 066 067 070 >-./012345678<
9 : ; < = > ? @ A B C D
071 072 073 074 075 076 077 100 101 102 103 104 >9:;<=>?@ABCD<
E F G H I J K L M N O P
105 106 107 110 111 112 113 114 115 116 117 120 >EFGHIJKLMNOP<
Q R S T U V W X Y Z [ \
121 122 123 124 125 126 127 130 131 132 133 134 >QRSTUVWXYZ[\<
] ^ _ ` a b c d e f g h
135 136 137 140 141 142 143 144 145 146 147 150 >]^_`abcdefgh<
i j k l m n o p q r s t
151 152 153 154 155 156 157 160 161 162 163 164 >ijklmnopqrst<
u v w x y z { | } ~
165 166 167 170 171 172 173 174 175 176 >uvwxyz{|}~<
blank
ht sp
011 040 >. <
cntrl
nul soh stx etx eot enq ack bel bs ht nl vt
000 001 002 003 004 005 006 007 010 011 012 013 >............<
ff cr so si dle dc1 dc2 dc3 dc4 nak syn etb
014 015 016 017 020 021 022 023 024 025 026 027 >............<
can em sub esc fs gs rs us del
030 031 032 033 034 035 036 037 177 >.........<
punct
! " # $ % & ' ( ) * + ,
041 042 043 044 045 046 047 050 051 052 053 054 >!"#$%&'()*+,<
- . / : ; < = > ? @ [ \
055 056 057 072 073 074 075 076 077 100 133 134 >-./:;<=>?@[\<
] ^ _ ` { | } ~
135 136 137 140 173 174 175 176 >]^_`{|}~<
alnum
0 1 2 3 4 5 6 7 8 9 A B
060 061 062 063 064 065 066 067 070 071 101 102 >0123456789AB<
C D E F G H I J K L M N
103 104 105 106 107 110 111 112 113 114 115 116 >CDEFGHIJKLMN<
O P Q R S T U V W X Y Z
117 120 121 122 123 124 125 126 127 130 131 132 >OPQRSTUVWXYZ<
a b c d e f g h i j k l
141 142 143 144 145 146 147 150 151 152 153 154 >abcdefghijkl<
m n o p q r s t u v w x
155 156 157 160 161 162 163 164 165 166 167 170 >mnopqrstuvwx<
y z
程序员的API
正如我在下面演示的那样,recode
将为您提供完整的字符图。根据其手册,它首先根据DEFAULT_CHARSET
环境变量的当前值执行此操作,否则,它将完全按照您指定的方式运行:
当省略字符集名称或将其保留为空时,将DEFAULT_CHARSET
改用环境中的变量值。如果未定义此变量,则recode
库使用当前语言环境的编码。在符合POSIX的系统上,这取决于环境变量中的第一个非空值LC_ALL, LC_CTYPE, LANG
,可以通过以下命令确定locale charmap.
另外值得注意的recode
是,它是一个api:
命名的程序recode
只是其重新编码库的一个应用程序。重新编码库可单独用于其他C程序。熟悉编码库的一种好方法是熟悉recode
程序本身。
要在安装重新编码库后使用它,C程序需要有一行:
#include <recode.h>
为了进行国际友好的字符串比较,POSIX
和C
标准定义了以下strcoll()
功能:
该strcoll()
功能应比较指向的字符串s1
到字符串指向s2
,都解释为适合于当前语言环境的LC_COLLATE类别。
strcoll()
如果成功,该功能不得更改errno的设置。
由于没有保留返回值来指示错误,因此希望检查错误情况的应用程序应将errno设置为0,然后调用
strcoll()
,然后检查errno。
这是一个单独使用的示例:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[15];
char str2[15];
int ret;
strcpy(str1, "abc");
strcpy(str2, "ABC");
ret = strcoll(str1, str2);
if(ret > 0)
{
printf("str1 is less than str2");
}
else if(ret < 0)
{
printf("str2 is less than str1");
}
else
{
printf("str1 is equal to str2");
}
return(0);
}
关于POSIX
字符类,您已经注意到您使用C
API查找了这些字符类。对于unicode字符和类,可以使用recode's
dump-with-names charset获得所需的输出。再次从其手册中:
例如,该命令recode l2..full < input
暗示必须从Latin-2转换为UCS-2,因为dump-with-names仅从UCS-2连接。在这种情况下,recode
不会在转储中显示原始的
Latin-2代码,而仅显示相应的UCS-2值。举一个简单的例子,命令
echo 'Hello, world!' | recode us..dump
产生以下输出:
UCS2 Mne Description
0048 H latin capital letter h
0065 e latin small letter e
006C l latin small letter l
006C l latin small letter l
006F o latin small letter o
002C , comma
0020 SP space
0077 w latin small letter w
006F o latin small letter o
0072 r latin small letter r
006C l latin small letter l
0064 d latin small letter d
0021 ! exclamation mark
000A LF line feed (lf)
描述性注释以英语和ASCII给出,但是如果英语描述不可用,而法语则可用,则使用Latin-1给出法语描述。但是,如果
LANGUAGE
or LANG
环境变量以字母fr开头,则当两个描述均可用时,列表首选项将变为法语。
使用与上述类似的语法及其包含的测试数据集,我可以通过以下方式获得自己的字符图:
recode -q u8/test8..dump </dev/null
输出值
UCS2 Mne Description
0001 SH start of heading (soh)
0002 SX start of text (stx)
0003 EX end of text (etx)
...
002B + plus sign
002C , comma
002D - hyphen-minus
...
0043 C latin capital letter c
0044 D latin capital letter d
0045 E latin capital letter e
...
006B k latin small letter k
006C l latin small letter l
006D m latin small letter m
...
007B (! left curly bracket
007C !! vertical line
007D !) right curly bracket
007E '? tilde
007F DT delete (del)
但是对于普通字符,recode
显然没有必要。这应该为您以128字节字符集中的所有内容命名为chars:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
输出值
001 002 003 004 005 006 007 010 011 012 013 014 >............<
soh stx etx eot enq ack bel bs ht nl vt ff
...
171 172 173 174 175 176 177 >yz{|}~.<
y z { | } ~ del
当然,仅表示128个字节,但这是因为我的语言环境(是否为utf-8字符集)都使用ASCII字符集,仅此而已。这就是我所得到的。如果我运行它但未对其进行luit
过滤,od
则会将其回滚并再次打印相同的地图,直到\0400.
但是,上述方法存在两个主要问题。首先是系统的整理顺序-对于非ASCII语言环境,字符集的位值不仅仅在seq
影响中,我认为这很可能是您要解决的问题的核心。
好吧,GNU tr's man
页面指出它将[:upper:]
[:lower:]
按顺序扩展类-但这不是很多。
我想象可以使用一些繁重的解决方案,sort
但是对于后端编程API而言,这将是一个相当笨拙的工具。
recode
会正确地执行此操作,但是前几天您似乎不太爱该程序。也许今天的编辑会对它产生更友好的影响。
GNU还提供了gettext
函数库,它似乎至少能够针对上下文解决此问题LC_MESSAGES
:
-功能:char * bind_textdomain_codeset
(const char *domainname,
const char *codeset
)
该bind_textdomain_codeset
功能可用于为域domainname的邮件目录指定输出字符集
。该代码集参数必须是一个有效的代码集可用于名iconv_open子功能,或一个空指针。
如果codeset参数为空指针,则bind_textdomain_codeset
返回名称为domainname的域
的当前选定代码集。如果尚未选择任何代码集,则返回NULL 。
该bind_textdomain_codeset
功能可以使用多次。如果使用相同的domainname参数多次使用,则后面的调用将覆盖前面的设置。
该bind_textdomain_codeset
函数返回一个指向包含所选代码集名称的字符串的指针。字符串是在函数内部分配的,用户不能更改。如果在执行期间系统退出内核
bind_textdomain_codeset
,则返回值为NULL,并相应地设置了全局变量errno。
您可能还使用本机Unicode字符类别,它们与语言无关,并且完全放弃了POSIX类,或者也许要调用前者来提供足够的信息来定义后者。
除了复杂之外,Unicode还带来了新的可能性。一种是每个Unicode字符都属于某个类别。您可以使用来匹配属于“字母”类别的单个字符
\p{L}
。您可以使用来匹配不属于该类别的单个字符\P{L}
。
同样,“字符”实际上意味着“ Unicode代码点”。\p{L}
匹配类别“字母”中的单个代码点。如果您输入的字符串à
编码为U+0061 U+0300
,则匹配时a
不带重音。如果输入à
编码为U+00E0
,则它与à
重音匹配。原因是两个代码点U+0061 (a)
和U+00E0 (à)
都在“字母”类别中,而U+0300
在“标记”类别中。
您现在应该了解为什么\P{M}\p{M}*+
等效于\X
。
\P{M}
匹配非组合标记的代码点,而\p{M}*+
匹配零个或多个组合标记的代码点。要匹配包括任何变音符号的字母,请使用\p{L}\p{M}*+
。最后一个正则表达式将始终匹配à
,无论其如何编码。所有格量词可确保在没有\P{M}\p{M}*+
非标记之后的组合标记的情况下,回溯不会导致与非标记相匹配,这\X
将永远不会。
提供上述信息的网站也讨论了Tcl
自己的POSIX兼容正则表达式实现,这可能是实现目标的另一种方法。
最后,在解决方案中,我建议您可以查询LC_COLLATE
文件本身,以获取完整且有序的系统字符映射。这看起来似乎并不容易,但是在将其编译如下后,我在以下方面取得了一些成功localedef
:
<LC_COLLATE od -j2K -a -w2048 -v |
tail -n2 |
cut -d' ' -f$(seq -s',' 4 2 2048) |
sed 's/nul\|\\0//g;s/ */ /g;:s;
s/\([^ ]\{1,3\}\) \1/\1/;ts;
s/\(\([^ ][^ ]* *\)\{16\}\)/\1\n/g'
dc1 dc2 dc3 dc4 nak syn etb can c fs c rs c sp ! "
# $ % & ' ( ) * + , - . / 0 1 2
3 4 5 6 7 8 9 : ; < = > ? @ A B
C D E F G H I J K L M N O P Q R
S T U V W X Y Z [ \ ] ^ _ ` a b
c d e f g h i j k l m n o p q r
s t u v w x y z { | } ~ del soh stx etx
eot enq ack bel c ht c vt cr c si dle dc1 del
诚然,它目前存在缺陷,但我希望它至少证明了这种可能性。
乍一看
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
看起来确实不多,但是随后我开始注意到copy
整个列表中的命令。例如,上面的文件似乎copy
在“ en_US”中,似乎它们在某种程度上都共享的另一个真正的大文件是iso_14651_t1_common
。
它相当大:
strings $_ | wc -c
#OUTPUT
431545
这是简介/usr/share/i18n/locales/POSIX
:
# Territory:
# Revision: 1.1
# Date: 1997-03-15
# Application: general
# Users: general
# Repertoiremap: POSIX
# Charset: ISO646:1993
# Distribution and use is free, also for
# commercial purposes.
LC_CTYPE
# The following is the POSIX Locale LC_CTYPE.
# "alpha" is by default "upper" and "lower"
# "alnum" is by definiton "alpha" and "digit"
# "print" is by default "alnum", "punct" and the <U0020> character
# "graph" is by default "alnum" and "punct"
upper <U0041>;<U0042>;<U0043>;<U0044>;<U0045>;<U0046>;<U0047>;<U0048>;\
<U0049>;<U004A>;<U004B>;<U004C>;<U004D>;<U004E>;<U004F>;
...
您grep
当然可以解决,但您可能只是:
recode -lf gb
代替。你会得到这样的东西:
Dec Oct Hex UCS2 Mne BS_4730
0 000 00 0000 NU null (nul)
1 001 01 0001 SH start of heading (soh)
...
... 和更多
我猜还有一个luit
终端UTF-8 pty
转换设备,可以在没有UTF-8支持的情况下对XTerms起到过渡作用。它处理许多开关-例如将所有转换后的字节记录到文件中或-c
作为简单的|pipe
过滤器。
我从来没有意识到这一点太多了-语言环境和字符映射表以及所有这些。这显然是很重要的事情,但我想一切都在幕后进行。至少在我的系统上有数百个man 3
与语言环境相关的搜索的相关结果。
还有:
zcat /usr/share/i18n/charmaps/UTF-8*gz | less
CHARMAP
<U0000> /x00 NULL
<U0001> /x01 START OF HEADING
<U0002> /x02 START OF TEXT
<U0003> /x03 END OF TEXT
<U0004> /x04 END OF TRANSMISSION
<U0005> /x05 ENQUIRY
...
那将持续很长时间。
这些Xlib
功能一直在处理- luit
是该程序包的一部分。
这些Tcl_uni...
功能也可能很有用。
仅需一点点<tab>
完成和man
搜索,我就已经在这个主题上学到了很多东西。
使用localedef
-您可以locales
在I18N
目录中编译。输出是时髦的,并且不是特别有用-根本不像charmaps
-,但是您可以像我在上面指定的那样获得原始格式:
mkdir -p dir && cd $_ ; localedef -f UTF-8 -i en_GB ./
ls -l
total 1508
drwxr-xr-x 1 mikeserv mikeserv 30 May 6 18:35 LC_MESSAGES
-rw-r--r-- 1 mikeserv mikeserv 146 May 6 18:35 LC_ADDRESS
-rw-r--r-- 1 mikeserv mikeserv 1243766 May 6 18:35 LC_COLLATE
-rw-r--r-- 1 mikeserv mikeserv 256420 May 6 18:35 LC_CTYPE
-rw-r--r-- 1 mikeserv mikeserv 376 May 6 18:35 LC_IDENTIFICATION
-rw-r--r-- 1 mikeserv mikeserv 23 May 6 18:35 LC_MEASUREMENT
-rw-r--r-- 1 mikeserv mikeserv 290 May 6 18:35 LC_MONETARY
-rw-r--r-- 1 mikeserv mikeserv 77 May 6 18:35 LC_NAME
-rw-r--r-- 1 mikeserv mikeserv 54 May 6 18:35 LC_NUMERIC
-rw-r--r-- 1 mikeserv mikeserv 34 May 6 18:35 LC_PAPER
-rw-r--r-- 1 mikeserv mikeserv 56 May 6 18:35 LC_TELEPHONE
-rw-r--r-- 1 mikeserv mikeserv 2470 May 6 18:35 LC_TIME
然后,od
您可以读取它-字节和字符串:
od -An -a -t u1z -w12 LC_COLLATE | less
etb dle enq sp dc3 nul nul nul T nul nul nul
23 16 5 32 19 0 0 0 84 0 0 0 >... ....T...<
...
虽然距离赢得选美比赛还有很长的路要走,但这是可用的输出。而且od
,你希望它是为好,当然是可配置的。
我想我也忘记了这些:
perl -mLocale
-- Perl module --
Locale::Codes Locale::Codes::LangFam Locale::Codes::Script_Retired
Locale::Codes::Constants Locale::Codes::LangFam_Codes Locale::Country
Locale::Codes::Country Locale::Codes::LangFam_Retired Locale::Currency
Locale::Codes::Country_Codes Locale::Codes::LangVar Locale::Language
Locale::Codes::Country_Retired Locale::Codes::LangVar_Codes Locale::Maketext
Locale::Codes::Currency Locale::Codes::LangVar_Retired Locale::Maketext::Guts
Locale::Codes::Currency_Codes Locale::Codes::Language Locale::Maketext::GutsLoader
Locale::Codes::Currency_Retired Locale::Codes::Language_Codes Locale::Maketext::Simple
Locale::Codes::LangExt Locale::Codes::Language_Retired Locale::Script
Locale::Codes::LangExt_Codes Locale::Codes::Script Locale::gettext
Locale::Codes::LangExt_Retired Locale::Codes::Script_Codes locale
我可能忘了他们,因为我无法让他们工作。我从不使用Perl
,我也不知道如何正确加载模块。但是man
页面看起来不错。无论如何,有什么告诉我,您会发现调用Perl模块的难度至少比我要难得多。而且,这些都已经在我的计算机上了-我什至从未使用过Perl。尤其是I18N
我有些不知所措,因为我非常了解我也不会让他们工作。
/usr/share/i18n/locales/i18n
……当然,其中大部分来自Unicode字符数据库。当然,拥有一个命令会很不错