假设您使用正常的操作系统(意思是,不是 Windows),使用 C99/C11 语言环境和宽字符支持很容易实现。考虑 filter.c:
#include <stdlib.h>
#include <locale.h>
#include <wchar.h>
#include <stdio.h>
wint_t convert(const wint_t wc)
{
switch (wc) {
case L'ῶ': return L'ώ';
default: return wc;
}
}
int main(void)
{
wint_t wc;
if (!setlocale(LC_ALL, "")) {
fprintf(stderr, "Current locale is unsupported.\n");
return EXIT_FAILURE;
}
if (fwide(stdin, 1) <= 0) {
fprintf(stderr, "Standard input does not support wide characters.\n");
return EXIT_FAILURE;
}
if (fwide(stdout, 1) <= 0) {
fprintf(stderr, "Standard output does not support wide characters.\n");
return EXIT_FAILURE;
}
while ((wc = fgetwc(stdin)) != WEOF)
fputwc(convert(wc), stdout);
return EXIT_SUCCESS;
}
上述程序读取标准输入,将每个ῶ转换为ώ,并输出结果。
注意宽字符串和字符都有L前缀; L'ῶ' 是一个宽字符常量。如果执行字符集(编译代码的字符集)是 Unicode,则这些仅在 Unicode 中,这取决于您的开发环境。 (幸运的是,在 Windows 之外,UTF-8 现在几乎是一种标准——and that is a good thing——所以像上面这样的代码就可以了。)
在 POSIXy 系统(如 Linux、Android、Mac OS、BSD)上,您可以使用 iconv() 工具将任何输入字符集转换为 Unicode,在此处进行转换,最后再转换回任何输出字符集.不幸的是,该问题未标记为posix,因此不在此特定问题范围内。
上面的例子使用了一个简单的 switch/case 语句。如果有很多替换对,可以使用例如
typedef struct {
wint_t from;
wint_t to;
} widepair;
static widepair replace[] = {
{ L'ῶ', L'ώ' },
/* Others? */
};
#define NUM_REPLACE (sizeof replace / sizeof replace[0])
在运行时,对replace[] 进行排序(使用qsort() 和一个比较from 元素的函数),并使用二分搜索快速确定是否要替换宽字符(如果是,替换为哪个宽字符)。因为这是一个 O(log2N) 操作,其中 N 是对的数量,它利用缓存没问题,甚至是数千个替换这种方式对不是问题。 (当然,您也可以在运行时构建替换数组,甚至通过用户输入或命令行选项。)
对于 Unicode 字符,我们可以使用uint32_t map_to[0x110000]; 将每个代码点直接映射到另一个 Unicode 代码点,但是因为我们不知道宽字符是否是 Unicode,所以我们不能这样做;直到编译时我们才知道宽字符的代码范围。当然,我们也可以进行多阶段编译,测试程序生成如上所示的replace[]数组,并以十进制输出它们的代码;然后进行某种自动分组或聚类,例如位图或哈希表,以“更快”完成。
但是,在实践中,I/O(读取和写入数据)通常比转换本身花费更多的实际时间。即使转化是瓶颈,转化率对大多数人来说也足够了。 (例如,使用 GNU 实用程序编译 C 或 C++ 代码时,预处理器首先在内部将源代码转换为 UTF-8。)