iconvトランスコードプログラミングの概要
glibcはトランスコード関数iconvを持っていて、使いやすくて、識別できるコードが多くて、プログラムが符号化間の変換に関わる必要がある場合は、それを使うことを考慮することができます.iconvコマンドの使い方.
iconvプログラミングは、次のglibcライブラリの呼び出しに関連します.
パラメータcdはiconv_でOpenは、返されるトランスコードハンドルを呼び出す.パラメータinbufは、トランスコードが必要なバッファを指す.パラメータinbytesleftはinbufが保存するトランスコードが必要なバイト数である.パラメータoutbufはトランスコード結果を格納する.パラメータoutbytesleftはoutbuf空間のサイズを格納します.
呼び出しに成功した場合、iconv変換を返すバイト数(不可逆呼び出しバイト数、可逆呼び出しバイト数は含まれません).そうでない場合は-1を返し、対応するerrnoを設定します.iconvはinbufを逐次スキャンし、文字を変換するたびにinbufを増やし、inbytesleftを減らし、結果をoutbufに格納し、結果バイト数をoutbytesleftに格納します.以下の場合、スキャンを停止して戻ります.
1.マルチバイトシーケンスが無効である場合、errnoはEILSEQであり、*inbufは最初の無効な文字を指す.2.inbufにバイトが残っているが、errnoはEINVALである.3.outbufスペースが足りなく、errnoはE 2 BIGである.4.正常な転換が整っている.
iconv関数の場合、2つの呼び出しがあります.
1.inbufまたは*inbufがNULL、outbufおよび*outbufがNULLでない場合、iconvは変換ステータスを初期状態に設定し、*outbufに変換シーケンスを保存します.outbufスペースが不足している場合、errnoはE 2 BIGに設定され、戻ります(size_t)(-1);2.inbufまたは*inbufはNULL、outbufまたは*outbufもNULL、iconvは遷移状態を初期状態に設定します.
iconvコマンドの使用は便利ですが、変換中に問題が発生すると変換が停止し、変換できないバイトシーケンスをスキップして変換を継続したい場合があります.次のプログラムでは、この機能を実現できます.
/** * siconv.cpp - A simple way to demostrate the usage of iconv calling** Report bugs to [email protected] * July 15th, 2006*/#include#include #include #include #include #include #include #include #include
#ifdef DEBUG#define TRACE(fmt, args...) fprintf(stderr, "%s:%s:%d:"fmt,\__FILE__, __FUNCTION__, __LINE__, ##args)#else#define TRACE(fmt, args...)#endif
#define CONVBUF_SIZE 32767
extern int errno;
void print_err(const char *fmt, ...){ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);}
int print_out(const char* buf, size_t num){ if (num != fwrite(buf, 1, num, stdout)) { return -1; } return 0;}
void print_usage() { print_err("Usage: siconv -f encoding -t encoding [-c] " "input-file");}
int iconv_string(const char *from, const char *to, const char *src, size_t len, ::std::string& result, int c = 0, size_t buf_size = 512){ iconv_t cd; char *pinbuf = const_cast< char* >(src); size_t inbytesleft = len; char *poutbuf = NULL; size_t outbytesleft = buf_size; char *dst = NULL; size_t retbytes = 0; int done = 0; int errno_save = 0; if ((iconv_t)-1 == (cd = iconv_open(to, from))) { return -1; } dst = new char[buf_size]; while(inbytesleft > 0 && !done) { poutbuf = dst; outbytesleft = buf_size; TRACE("TARGET - in:%p pin:%p left:%d", src, pinbuf, inbytesleft); retbytes = iconv(cd, &pinbuf, &inbytesleft, &poutbuf, &outbytesleft); errno_save = errno; if (dst != poutbuf) {//we have something to write TRACE("OK - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); result.append(dst, poutbuf-dst); } if (retbytes != (size_t)-1) { poutbuf = dst; outbytesleft = buf_size; (void)iconv(cd, NULL, NULL, &poutbuf, &outbytesleft); if (dst != poutbuf) {//we have something to write TRACE("OK - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); result.append(dst, poutbuf-dst); } errno_save = 0; break; } TRACE("FAIL - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); switch(errno_save) { case E2BIG: TRACE("E E2BIG"); break; case EILSEQ: TRACE("E EILSEQ"); if (c) { errno_save = 0; inbytesleft = len-(pinbuf-src);//forward one illegal byte inbytesleft--; pinbuf++; break; } done = 1; break; case EINVAL: TRACE("E EINVAL"); done = 1; break; default: TRACE("E Unknown:[%d]%s", errno, strerror(errno)); done = 1; break; } } delete[] dst; iconv_close(cd); errno = errno_save; return (errno_save) ? -1 : 0;}
int conv_file_fd(const char* from, const char *to, int fd, ::std::string& result, int c){ struct stat st; void *start; if (0 != fstat(fd, &st)) { return -1; } start = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED == start) { return -1; } if (iconv_string(from, to, (char*)start, st.st_size, result, c, CONVBUF_SIZE) < 0) { int errno_save = errno; munmap(start, st.st_size); TRACE(""); errno = errno_save; return -1; } munmap(start, st.st_size); return 0;}
int conv_file(const char* from, const char* to, const char* filename, int c){ ::std::string result; FILE *fp; if (NULL == (fp=fopen(filename, "rb"))) { print_err("open file %s:[%d]%s", filename, errno, strerror(errno)); return -1; } if (conv_file_fd(from, to, fileno(fp), result, c) < 0) { print_err("conv file fd:[%d]%s", errno, strerror(errno)); fclose(fp); return -1; } print_out(result.data(), result.size()); fclose(fp); return 0;}
int main(int argc,char*argv[]){#ifdef TESTCASE::std::string strA=「ようこそ(welcome^^)首都北京へ.」::std::string strB=「We are chinese<=>私たちは中国人です」と叫ぶ. ::std::string strC = strA.substr(0, 20) + strB.substr(0, 41); ::std::string result; if (iconv_string("GBK", "UTF-8", strC.data(), strC.size(), result, 1) < 0) { TRACE("ERROR [%d]%s", errno, strerror(errno)); } TRACE("CONVERSION RESULT:"); result.append(""); print_out(result.data(), result.size()); return 0;#else ::std::string from, to; ::std::string input_file; int o; int c = 0; while (-1 != (c = getopt(argc, argv, "f:t:c"))) { switch(c) { case 'f': from = optarg; break; case 't': to = optarg; break; case 'c': c = 1; break; default: return -1; } } if (from.empty() || to.empty() || optind != (argc-1)) { print_usage(); return -1; } input_file = argv[optind++]; return conv_file(from.c_str(), to.c_str(), input_file.c_str(), c);#endif}
ファイルが大きすぎてメモリバッファが足りない場合は、メモリイメージファイルで解決できます.iconvコマンドに対して、-cオプションを追加して、変換中に発生する可能性のある問題を無視します.
コマンドラインに-DDEBUGオプションを追加すると、デバッグ文がコンパイルされます.-DTESTCASEオプションを追加すると、iconv_のみコンパイルされます.string関数テストの場合.
$ iconv --list #
$ iconv -f GB2312 -t UTF-8 a.html > b.html # GB2312 a.html UTF-8 , b.html
$ iconv -f GB2312 -t BIG5 a.html > b.html # GB2312 a.html BIG5 , b.html
iconvプログラミングは、次のglibcライブラリの呼び出しに関連します.
#include <iconv.h> iconv_t iconv_open(const char *tocode, const char *fromcode); int iconv_close(iconv_t cd); size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
iconv , iconv_open , iconv , iconv_close 。
iconv :
パラメータcdはiconv_でOpenは、返されるトランスコードハンドルを呼び出す.パラメータinbufは、トランスコードが必要なバッファを指す.パラメータinbytesleftはinbufが保存するトランスコードが必要なバイト数である.パラメータoutbufはトランスコード結果を格納する.パラメータoutbytesleftはoutbuf空間のサイズを格納します.
呼び出しに成功した場合、iconv変換を返すバイト数(不可逆呼び出しバイト数、可逆呼び出しバイト数は含まれません).そうでない場合は-1を返し、対応するerrnoを設定します.iconvはinbufを逐次スキャンし、文字を変換するたびにinbufを増やし、inbytesleftを減らし、結果をoutbufに格納し、結果バイト数をoutbytesleftに格納します.以下の場合、スキャンを停止して戻ります.
1.マルチバイトシーケンスが無効である場合、errnoはEILSEQであり、*inbufは最初の無効な文字を指す.2.inbufにバイトが残っているが、errnoはEINVALである.3.outbufスペースが足りなく、errnoはE 2 BIGである.4.正常な転換が整っている.
iconv関数の場合、2つの呼び出しがあります.
1.inbufまたは*inbufがNULL、outbufおよび*outbufがNULLでない場合、iconvは変換ステータスを初期状態に設定し、*outbufに変換シーケンスを保存します.outbufスペースが不足している場合、errnoはE 2 BIGに設定され、戻ります(size_t)(-1);2.inbufまたは*inbufはNULL、outbufまたは*outbufもNULL、iconvは遷移状態を初期状態に設定します.
iconvコマンドの使用は便利ですが、変換中に問題が発生すると変換が停止し、変換できないバイトシーケンスをスキップして変換を継続したい場合があります.次のプログラムでは、この機能を実現できます.
/** * siconv.cpp - A simple way to demostrate the usage of iconv calling** Report bugs to [email protected] * July 15th, 2006*/#include
#ifdef DEBUG#define TRACE(fmt, args...) fprintf(stderr, "%s:%s:%d:"fmt,\__FILE__, __FUNCTION__, __LINE__, ##args)#else#define TRACE(fmt, args...)#endif
#define CONVBUF_SIZE 32767
extern int errno;
void print_err(const char *fmt, ...){ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap);}
int print_out(const char* buf, size_t num){ if (num != fwrite(buf, 1, num, stdout)) { return -1; } return 0;}
void print_usage() { print_err("Usage: siconv -f encoding -t encoding [-c] " "input-file");}
int iconv_string(const char *from, const char *to, const char *src, size_t len, ::std::string& result, int c = 0, size_t buf_size = 512){ iconv_t cd; char *pinbuf = const_cast< char* >(src); size_t inbytesleft = len; char *poutbuf = NULL; size_t outbytesleft = buf_size; char *dst = NULL; size_t retbytes = 0; int done = 0; int errno_save = 0; if ((iconv_t)-1 == (cd = iconv_open(to, from))) { return -1; } dst = new char[buf_size]; while(inbytesleft > 0 && !done) { poutbuf = dst; outbytesleft = buf_size; TRACE("TARGET - in:%p pin:%p left:%d", src, pinbuf, inbytesleft); retbytes = iconv(cd, &pinbuf, &inbytesleft, &poutbuf, &outbytesleft); errno_save = errno; if (dst != poutbuf) {//we have something to write TRACE("OK - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); result.append(dst, poutbuf-dst); } if (retbytes != (size_t)-1) { poutbuf = dst; outbytesleft = buf_size; (void)iconv(cd, NULL, NULL, &poutbuf, &outbytesleft); if (dst != poutbuf) {//we have something to write TRACE("OK - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); result.append(dst, poutbuf-dst); } errno_save = 0; break; } TRACE("FAIL - in:%p pin:%p left:%d done:%d buf:%d", src, pinbuf, inbytesleft, pinbuf-src, poutbuf-dst); switch(errno_save) { case E2BIG: TRACE("E E2BIG"); break; case EILSEQ: TRACE("E EILSEQ"); if (c) { errno_save = 0; inbytesleft = len-(pinbuf-src);//forward one illegal byte inbytesleft--; pinbuf++; break; } done = 1; break; case EINVAL: TRACE("E EINVAL"); done = 1; break; default: TRACE("E Unknown:[%d]%s", errno, strerror(errno)); done = 1; break; } } delete[] dst; iconv_close(cd); errno = errno_save; return (errno_save) ? -1 : 0;}
int conv_file_fd(const char* from, const char *to, int fd, ::std::string& result, int c){ struct stat st; void *start; if (0 != fstat(fd, &st)) { return -1; } start = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); if (MAP_FAILED == start) { return -1; } if (iconv_string(from, to, (char*)start, st.st_size, result, c, CONVBUF_SIZE) < 0) { int errno_save = errno; munmap(start, st.st_size); TRACE(""); errno = errno_save; return -1; } munmap(start, st.st_size); return 0;}
int conv_file(const char* from, const char* to, const char* filename, int c){ ::std::string result; FILE *fp; if (NULL == (fp=fopen(filename, "rb"))) { print_err("open file %s:[%d]%s", filename, errno, strerror(errno)); return -1; } if (conv_file_fd(from, to, fileno(fp), result, c) < 0) { print_err("conv file fd:[%d]%s", errno, strerror(errno)); fclose(fp); return -1; } print_out(result.data(), result.size()); fclose(fp); return 0;}
int main(int argc,char*argv[]){#ifdef TESTCASE::std::string strA=「ようこそ(welcome^^)首都北京へ.」::std::string strB=「We are chinese<=>私たちは中国人です」と叫ぶ. ::std::string strC = strA.substr(0, 20) + strB.substr(0, 41); ::std::string result; if (iconv_string("GBK", "UTF-8", strC.data(), strC.size(), result, 1) < 0) { TRACE("ERROR [%d]%s", errno, strerror(errno)); } TRACE("CONVERSION RESULT:"); result.append(""); print_out(result.data(), result.size()); return 0;#else ::std::string from, to; ::std::string input_file; int o; int c = 0; while (-1 != (c = getopt(argc, argv, "f:t:c"))) { switch(c) { case 'f': from = optarg; break; case 't': to = optarg; break; case 'c': c = 1; break; default: return -1; } } if (from.empty() || to.empty() || optind != (argc-1)) { print_usage(); return -1; } input_file = argv[optind++]; return conv_file(from.c_str(), to.c_str(), input_file.c_str(), c);#endif}
ファイルが大きすぎてメモリバッファが足りない場合は、メモリイメージファイルで解決できます.iconvコマンドに対して、-cオプションを追加して、変換中に発生する可能性のある問題を無視します.
$ g++ -o siconv siconv.cpp
コマンドラインに-DDEBUGオプションを追加すると、デバッグ文がコンパイルされます.-DTESTCASEオプションを追加すると、iconv_のみコンパイルされます.string関数テストの場合.