由于工作需要,在我的powerpc嵌入式板子上需要用到iconv库函数把UTF-8转化为GB2312,但是可能是因为gcc库中自带的iconv不完善,导致转化结果始终为空(相同的代码在我的linux主机上运行就一切正常)。于是就打算自己下载一个libiconv库交叉编译来使用。
从网上下载了libiconv-1.14,编译后发现生成的库文件有1.2M之多,感觉太大了,自己认为在库中数据占很大部分,因为iconv支持世界上几乎所有常见语言的编码转换,但是我只需要UTF-8转换为GB2312,所以有必要裁剪一下。
在对libiconv-1.14裁剪时遇到了很多错误,特别是canonical.h的一大串错误,感觉canonical.h是由其它地方生成的文件,不太好解决,于是决定下载个低版本的libiconv试试,结果还真成功了,下面写一下自己裁剪的过程。
1. 从GNU网站下载libiconv-1.8.tar.gz (http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.8.tar.gz 或者 交大开源镜像http://mirror.bjtu.edu.cn/gnu/libiconv/libiconv-1.8.tar.gz)
2. 下载后解压缩,进入libiconv-1.8目录。
cd libiconv-1.8
新建一个目录作为安装路径:
mkdir iconv
3.修改源文件。
需要修改的文件只有两个,都在lib目录下,一个为aliases.gperf,另一个为encodings.def。
首先打开aliases.gperf,自上到下,直到CSISOLATIN1, ei_iso8859_1这一行,这些是需要保留的,然后剩下的就可以自由裁减了。
由于我只需要GB2312,所以后面的部分我只保留了下面几行:
EUC-CN, ei_euc_cn EUCCN, ei_euc_cn GB2312, ei_euc_cn CN-GB, ei_euc_cn CSGB2312, ei_euc_cn其他的行全部删掉了,然后保存,修改后的 aliases.gperf文件如下所示:
struct alias { const char* name; unsigned int encoding_index; }; %% US-ASCII, ei_ascii ASCII, ei_ascii ISO646-US, ei_ascii ISO_646.IRV:1991, ei_ascii ISO-IR-6, ei_ascii ANSI_X3.4-1968, ei_ascii ANSI_X3.4-1986, ei_ascii CP367, ei_ascii IBM367, ei_ascii US, ei_ascii CSASCII, ei_ascii UTF-8, ei_utf8 UCS-2, ei_ucs2 ISO-10646-UCS-2, ei_ucs2 CSUNICODE, ei_ucs2 UCS-2BE, ei_ucs2be UNICODEBIG, ei_ucs2be UNICODE-1-1, ei_ucs2be CSUNICODE11, ei_ucs2be UCS-2LE, ei_ucs2le UNICODELITTLE, ei_ucs2le UCS-4, ei_ucs4 ISO-10646-UCS-4, ei_ucs4 CSUCS4, ei_ucs4 UCS-4BE, ei_ucs4be UCS-4LE, ei_ucs4le UTF-16, ei_utf16 UTF-16BE, ei_utf16be UTF-16LE, ei_utf16le UTF-32, ei_utf32 UTF-32BE, ei_utf32be UTF-32LE, ei_utf32le UTF-7, ei_utf7 UNICODE-1-1-UTF-7, ei_utf7 CSUNICODE11UTF7, ei_utf7 UCS-2-INTERNAL, ei_ucs2internal UCS-2-SWAPPED, ei_ucs2swapped UCS-4-INTERNAL, ei_ucs4internal UCS-4-SWAPPED, ei_ucs4swapped C99, ei_c99 JAVA, ei_java ISO-8859-1, ei_iso8859_1 ISO_8859-1, ei_iso8859_1 ISO_8859-1:1987, ei_iso8859_1 ISO-IR-100, ei_iso8859_1 CP819, ei_iso8859_1 IBM819, ei_iso8859_1 LATIN1, ei_iso8859_1 L1, ei_iso8859_1 CSISOLATIN1, ei_iso8859_1 EUC-CN, ei_euc_cn EUCCN, ei_euc_cn GB2312, ei_euc_cn CN-GB, ei_euc_cn CSGB2312, ei_euc_cn
DEFENCODING(( "ISO-8859-1", /* IANA */ "ISO_8859-1", /* IANA */ "ISO_8859-1:1987", /* IANA */ "ISO-IR-100", /* IANA */ "CP819", /* IANA */ "IBM819", /* IANA */ "LATIN1", /* IANA */ "L1", /* IANA */ "csISOLatin1", /* IANA */ /*"ISO8859-1", X11R6.4, glibc */ /*"ISO8859_1", JDK 1.1 */ ), iso8859_1, { iso8859_1_mbtowc, NULL }, { iso8859_1_wctomb, NULL })
这一部分,上面所有的是需要保留的(包括这一部分)。后面再保留:
DEFENCODING(( "EUC-CN", /* glibc */ "EUCCN", /* glibc */ "GB2312", /* IANA */ "CN-GB", /* RFC 1922 */ "csGB2312", /* IANA */ /*"EUC_CN", JDK 1.1 */ ), euc_cn, { euc_cn_mbtowc, NULL }, { euc_cn_wctomb, NULL })
这个是关于GB2312编码的。剩余的部分可以全部删除了,这与aliases.gperf对应起来了。
4. 由aliases.gperf重新生成aliases.h。
aliases.gperf文件的目的是为了生成aliases.h。打开aliases.h,发现前面几行有些注释:
/* ANSI-C code produced by gperf version 3.0.3 */
/* Command-line: gperf -t -L ANSI-C -H aliases_hash -N aliases_lookup -G -W aliases -7 -C -k '1,3-11,$' -i 1 aliases.gperf */
看到了生成aliases.h的命令行,于是在lib目录下,执行:
gperf -t -L ANSI-C -H aliases_hash -N aliases_lookup -G -W aliases -7 -C -k '1,3-11,$' -i 1 aliases.gperf > aliases.h
便可以了。
3. 配置,编译
进入libiconv-1.81的根目录,执行
./configure CC=powerpc-linux-gcc --target=powerpc-linux --host=powerpc-linux --enable-shared=yes --enable-static=yes --prefix=/opt/externel/libiconv-1.8/iconv
配置完成后,make;make install便可以了。
最后在iconv/lib目录下生成了我所需要的动态库和静态库(libiconv.so.2.1.0和libiconv.a)。
没有精简之前,生成的libiconv.so.2.1.0有1.1M,精简后只有220K。
注:上面配置过程中,--enable-static=yes是为了生成静态库libiconv.a,这样我在编译自己的程序时可以静态链接libiconv.a,把代码编译到自己的可执行程序中,就不再需要在运行时链接libiconv.so.2.1.0库了。
注:在使用时一定要用双引号包含头文件,不要用尖括号,否则编译器可能引用默认的库,导致执行时出问题(因为我的系统库glibc iconv这部分有问题)
#include "libiconv-1.8/include/iconv.h"
附我的转换函数:
#include <stdio.h> #include <string.h> #include <errno.h> #include "libiconv-1.8/include/iconv.h" /* 汉字UTF8编码到GB2312编码的转换函数 * 本函数只支持单个汉字的转换 * inbuf 汉字的UTF8编码存储区 * outbuf 转换完成后存储GB2312编码的缓冲区 * out_size outbuf的长度 * 返回值:成功则返回outbuf,失败返回NULL * * 注意:常用汉字UTF8编码长度为3个字节,本函数只支持3个字节 * 转换完成后的GB2312编码长度为2字节,因此outbuf长度至少为3个字节 */ static char* hz_utf8_to_gb2312_single(const char *inbuf,char *outbuf, size_t out_size) { char ** __restrict__ src = (char ** )&inbuf; char *dest = outbuf; size_t in_size = 3,size; iconv_t cd; // printf("%s:0x%X 0x%X 0x%X\n",__func__, // (unsigned char)inbuf[0], (unsigned char)inbuf[1],(unsigned char)inbuf[2]); if(out_size < 3) { printf("%s: out_size too small\n",__func__,strerror(errno)); return NULL; } memset(outbuf,0,3); cd = iconv_open("GB2312","UTF-8"); if(cd < 0) { printf("%s: iconv_open error:%s\n",__func__,strerror(errno)); return NULL; } size = iconv(cd,src,&in_size,&dest,&out_size); if(size < 0) { iconv_close(cd); printf("%s: iconv error:%s\n",__func__,strerror(errno)); return NULL; } iconv_close(cd); // printf("%s: 0x%X 0x%X\n",__func__,(unsigned char)outbuf[0],(unsigned char)outbuf[1]); return outbuf; }
# --------------------------------------------------------------------------- # platform dependencies # --------------------------------------------------------------------------- CC = powerpc-linux-gcc CXX = powerpc-linux-g++ # --------------------------------------------------------------------------- # project specifics # --------------------------------------------------------------------------- MAPFILE = iconv_test.map CFLAGS = -Wl,-Map,$(MAPFILE) LDLIBS = -lpthread ./libiconv-1.8/lib/libiconv.a TGT = iconv_test CSRC = iconv_test.c OBJS = $(CSRC:.c=.o) $(ASRC:.S=.o) DEPS = $(OBJS:.o=.d) $(NOLINK_OBJS:.o=.d) BIN = $(TGT) .PHONY: clean all all: $(BIN) $(BIN): $(OBJS) $(CC) $(CFLAGS) $(OBJS) $(LDLIBS) -o $@ clean: rm -f $(DEPS) rm -f $(OBJS) $(NOLINK_OBJS) rm -f $(BIN) rm -f $(MAPFILE) # --------------------------------------------------------------------------- # rules for code generation # --------------------------------------------------------------------------- %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< %.o: %.S $(CC) $(ASFLAGS) -o $@ -c $<