2023-08-29
libiconv (iconv) はヘッダファイルの数、ライブラリサイズも膨大な割に普通に文字化けしたりする。
もう1つの文字コード変換の定番「nkf」を使うべき。現在も以下サイトでメンテナンスされている。
https://ja.osdn.net/projects/nkf/
しかしながら現在、PerlおよびPython用にはライブラリ化されているが、C/C++言語から直接呼べるライブラリ化はされていないので、自作することにする。
といってもPython用にライブラリ化しているコードが大きなヒントになっており、これを少し改造するだけでC言語用ライブラリは出来上がる。
まずは最新版ソース一式をダウンロードする。上記サイトからnkf-2.1.5.tar.gzを探して落とすか、リポジトリをチェックアウトする。
git clone https://scm.osdn.net/gitroot/nkf/nkf.git
この中の、NKF.python3/NKF_python.cをもとに、libnkfを作成する。
チェックアウトしたnkfディレクトリと同じ階層に作成して../nkf/とソースを参照する形になる。
HOME
--nkfディレクトリ
--libnkfディレクトリ/libnkf.c,libnkf.h,Makefile
mkdir libnkf cp -p nkf/NKF.python3/NKF_python.c libnkf/libnkf.c cd libnkf
まず、ヘッダファイルlibnkf.hを作る。以下のような感じになる。
C/C++ | libnkf.h | GitHub Source |
/* Custom NKF Library for C/C++ from NKF.python3/NKF_python.c */ #ifndef __libnkf_h__ #define __libnkf_h__ #include <stdio.h> #include <string.h> #include <stdlib.h> #ifdef __cplusplus extern "C" { #endif extern unsigned char* nkf_convert(unsigned char* str, int strlen, char* opts, int optslen); extern const char* nkf_guess(unsigned char* str, int strlen); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* __libnkf_h__ */
nkf_convertが主役関数。NKFコマンドそのもの。
strに入力文字列、strlenにstrlen(str)、optsにNKFコマンドオプション、optslenにstrlen(opts)を指定して呼ぶ。
変換結果がメモリ確保されて返るので、呼び出し側でfreeすること。
nkf_guessは、入力文字列の文字コードを判定して結果を名前で返す関数。
strに入力文字列、strlenにstrlen(str)を指定して呼ぶ。結果はコード名文字列定数が返るので、freeの必要は無い。
そして、libnkf.cを編集する。以下のような感じ。NKF_python.c元コード部分はコメントアウトで残している。
基本的にPython専用の固有な記述をすべてコメントアウトし、malloc/freeは標準関数に置き換える。
../utf8tbl.c,nkf.cとincludeしている箇所があるので、これの階層を../nkfに変更する。
C/C++ | libnkf.c | GitHub Source |
/* Custom NKF Library for C/C++ from NKF.python3/NKF_python.c */ /* Changes. 2009.6.2 Remove WISH_TRUE, use get_guessed_code() for nkf-2.0.9 by SATOH Fumiyasu (fumiyas @ osstech co jp) 2008.7.17 Change the type of strlen from long to int, by SATOH Fumiyasu. 2007.2.1 Add guess() function. 2007.1.13 Remove pynkf_parseopts(), by SATOH Fumiyasu. */ /** Python Interface to NKF *************************************************************************** ** Copyright (c) 2005 Matsumoto, Tadashi <ma2@city.plala.jp> ** All Rights Reserved. ** ** Everyone is permitted to do anything on this program ** including copying, modifying, improving, ** as long as you don't try to pretend that you wrote it. ** i.e., the above copyright notice has to appear in all copies. ** Binary distribution requires original version messages. ** You don't have to ask before copying, redistribution or publishing. ** THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. ***************************************************************************/ /* #include "Python.h" */ #include "libnkf.h" #include <setjmp.h> #undef getc #undef ungetc #define getc(f) pynkf_getc(f) #define ungetc(c,f) pynkf_ungetc(c,f) #undef putchar #undef TRUE #undef FALSE #define putchar(c) pynkf_putchar(c) static int pynkf_ibufsize, pynkf_obufsize; static unsigned char *pynkf_inbuf, *pynkf_outbuf; static int pynkf_icount,pynkf_ocount; static unsigned char *pynkf_iptr, *pynkf_optr; static jmp_buf env; static int pynkf_guess_flag; static int pynkf_getc(FILE *f) { unsigned char c; if (pynkf_icount >= pynkf_ibufsize) return EOF; c = *pynkf_iptr++; pynkf_icount++; return (int)c; } static int pynkf_ungetc(int c, FILE *f) { if (pynkf_icount--){ *(--pynkf_iptr) = c; return c; }else{ return EOF; } } static void pynkf_putchar(int c) { size_t size; unsigned char *p; if (pynkf_guess_flag) { return; } if (pynkf_ocount--){ *pynkf_optr++ = c; }else{ size = pynkf_obufsize + pynkf_obufsize; /* p = (unsigned char *)PyMem_Realloc(pynkf_outbuf, size + 1); */ p = (unsigned char *)realloc(pynkf_outbuf, size + 1); if (pynkf_outbuf == NULL){ longjmp(env, 1); } pynkf_outbuf = p; pynkf_optr = pynkf_outbuf + pynkf_obufsize; pynkf_ocount = pynkf_obufsize; pynkf_obufsize = size; *pynkf_optr++ = c; pynkf_ocount--; } } #define PERL_XS 1 /* #include "../utf8tbl.c" #include "../nkf.c" */ #include "../nkf/utf8tbl.c" #include "../nkf/nkf.c" /* static PyObject * pynkf_convert(unsigned char* str, int strlen, char* opts, int optslen) */ extern unsigned char* nkf_convert(unsigned char* str, int strlen, char* opts, int optslen) { /* PyObject * res; */ pynkf_ibufsize = strlen + 1; pynkf_obufsize = pynkf_ibufsize * 1.5 + 256; /* pynkf_outbuf = (unsigned char *)PyMem_Malloc(pynkf_obufsize); */ pynkf_outbuf = (unsigned char *)malloc(pynkf_obufsize); if (pynkf_outbuf == NULL){ /* PyErr_NoMemory(); */ return NULL; } pynkf_outbuf[0] = '\0'; pynkf_ocount = pynkf_obufsize; pynkf_optr = pynkf_outbuf; pynkf_icount = 0; pynkf_inbuf = str; pynkf_iptr = pynkf_inbuf; pynkf_guess_flag = 0; if (setjmp(env) == 0){ reinit(); options(opts); kanji_convert(NULL); }else{ /* PyMem_Free(pynkf_outbuf); PyErr_NoMemory(); */ free(pynkf_outbuf); return NULL; } *pynkf_optr = 0; /* res = PyBytes_FromString(pynkf_outbuf); PyMem_Free(pynkf_outbuf); return res; */ return pynkf_outbuf; } /* static PyObject * pynkf_convert_guess(unsigned char* str, int strlen) */ extern const char* nkf_guess(unsigned char* str, int strlen) { /* PyObject * res; */ const char *codename; pynkf_ibufsize = strlen + 1; pynkf_icount = 0; pynkf_inbuf = str; pynkf_iptr = pynkf_inbuf; pynkf_guess_flag = 1; reinit(); guess_f = 1; kanji_convert(NULL); codename = get_guessed_code(); /* res = PyUnicode_FromString(codename); return res; */ return codename; } /* #ifndef EXTERN_NKF static #endif PyObject *pynkf_nkf(PyObject *self, PyObject *args) { unsigned char *str; int strlen; char *opts; int optslen; PyObject* res; if (!PyArg_ParseTuple(args, "s#y#", &opts, &optslen, &str, &strlen)) { return NULL; } res = pynkf_convert(str, strlen, opts, optslen); return res; } #ifndef EXTERN_NKF static #endif PyObject *pynkf_guess(PyObject *self, PyObject *args) { unsigned char *str; int strlen; PyObject* res; if (!PyArg_ParseTuple(args, "y#", &str, &strlen)) { return NULL; } res = pynkf_convert_guess(str, strlen); return res; } #ifndef EXTERN_NKF static PyMethodDef nkfMethods[] = { {"nkf", pynkf_nkf, METH_VARARGS, ""}, {"guess", pynkf_guess, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef nkfmodule = { PyModuleDef_HEAD_INIT, "nkf", "", -1, nkfMethods }; */ /* Module initialization function */ /* PyMODINIT_FUNC PyInit_nkf(void) { return PyModule_Create(&nkfmodule); } #endif */
ソースコードの準備ができたので、スタティックライブラリlibnkf.a、シェアードライブラリlibnkf.so.1.0をビルドするMakefileを以下のように作成する。
Shell | Makefile | GitHub Source |
CC = gcc #CC_DBG += -g CC_OPT += CC_INC += CFLAGS += $(CC_OPT) $(CC_DBG) $(CC_INC) DESTLIB = /usr/local/lib DESTINC = /usr/local/include all: libnkf.a libnkf.so.1.0 libnkf.oa: libnkf.c libnkf.h $(CC) $(CFLAGS) -o $@ -c $< libnkf.os: libnkf.c libnkf.h $(CC) $(CFLAGS) -fPIC -o $@ -c $< libnkf.a: libnkf.oa ar rv $@ $? ranlib $@ libnkf.so.1.0: libnkf.os $(CC) -shared -Wl,-soname,libnkf.so.1 -o libnkf.so.1.0 $? clean: rm -f *.o* *.a *.so* install: all install libnkf.a $(DESTLIB) install libnkf.so.1.0 $(DESTLIB) ldconfig $(DESTLIB) ln -f -s -r $(DESTLIB)/libnkf.so.1 $(DESTLIB)/libnkf.so install -m 0644 libnkf.h $(DESTINC)
gccコンパイルオプションの異なるスタティックとシェアードを同時に作るのでオブジェクトファイルも.oでなく.oa、.osなどと分ける必要がある。
シェアードライブラリをビルドする場合-shared -Wl,-soname,libxxx.so.1とするのはgccの固定ルールである。
make clean make gcc -o libnkf.oa -c libnkf.c ar rv libnkf.a libnkf.oa ar: libnkf.a を作成しています a - libnkf.oa ranlib libnkf.a gcc -fPIC -o libnkf.os -c libnkf.c gcc -shared -Wl,-soname,libnkf.so.1 -o libnkf.so.1.0 libnkf.os
簡単に使うなら、出来上がったlibnkf.aをそのままリンクして使うことができる。
gcc -o sample.x sample.c libnkf.a
システム/usr/localにヘッダとライブラリをインストールするなら、以下sudoで入れる。
sudo make install install libnkf.a /usr/local/lib install libnkf.so.1.0 /usr/local/lib ldconfig /usr/local/lib ln -f -s /usr/local/lib/libnkf.so.1 /usr/local/lib/libnkf.so install -m 0644 libnkf.h /usr/local/include
/usr/local/include/libnkf.h、/usr/local/lib/libnkf.aが入り、さらにシェアードライブラリlibnkf.so.1.0をコピー後ldconfigによりlibnkf.so.1が生成されるのでlibnkf.soとしてリンクが貼られて完了。
/usr/local/lib/libnkf.so,/usr/local/include/libnkf.hを利用する版のコンパイルは以下のようにする。
gcc -I/usr/local/include -L/usr/local/lib -o sample.x sample.c -lnkf
目的ライブラリをリンクしているかは以下コマンドで確認できる。
ldd sample.x linux-vdso.so.1 (0x00007ffe65729000) libnkf.so.1 => /usr/local/lib/libnkf.so.1 (0x00007ff4cb9da000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff4cb63b000) /lib64/ld-linux-x86-64.so.2 (0x00007ff4cbe2f000)
サンプルプログラムsample.cは以下のような感じでUTF-8で保存。
C/C++ | sample.c | GitHub Source |
#include "libnkf.h" int main(void) { const char *a = "アァイアイAB12AB12"; /* 半角・全角カナ交じり文字列 */ /* テスト1 UTF->SJIS変換 半角カナ->全角カナ変換 */ const char *o1 = "-W -s -X"; /* テスト2 UTF->SJIS変換 半角カナを全角にしない 全角を半角に 全角カナを半角カナに */ const char *o2 = "-W -s -x -Z -Z4"; char *b = NULL; printf("%s\n", nkf_guess((unsigned char*)a, strlen(a))); b = (char*)nkf_convert((unsigned char*)a, strlen(a), (char*)o1, strlen(o1)); if(b) { printf("%s\n", b); free(b); } b = (char*)nkf_convert((unsigned char*)a, strlen(a), (char*)o2, strlen(o2)); if(b) { printf("%s\n", b); free(b); } return 0; }
シフトJISに変換するサンプルなので、ターミナルの文字コードをSJISにして実行。
実行結果
./sample.x UTF-8 アァイアイAB12AB12 アァイアイAB12AB12
nkfコマンドを打ったのと全く同じ、期待通りの結果が得られている模様。
結果からもわかるように、元々、nkfには半角カナ以外の半角を全角にする機能は無い。
NKFコマンドオプションの詳細
入力文字コードの指定 -J : JIS -E : EUC -S : SJIS -W : UTF-8 出力文字コードの指定 -j : JIS -e : EUC -s : SJIS -w : UTF-8 改行コード変換指定 -Lu LFに統一 -Lw CRLFに統一 半角全角変換指定 -X 半角カナを全角カナにする -x 半角カナを全角カナにしない -Z 全角を半角にする -Z4 全角カナを半角カナにする
さらにサンプル
//EUC+LFをSJIS+CRLFに変換 char euc[128],*sjis; strcpy( euc,"あいうえお\n" ); sjis = (char*)nkf_convert( (unsigned char*)euc,strlen(euc),(char*)"-E -s -Lw",9 ); //SJIS+CRLFをUTF-8+LFに変換 char sjis[128],*utf; strcpy( sjis,"あいうえお\r\n" ); utf = (char*)nkf_convert( (unsigned char*)sjis,strlen(sjis),(char*)"-S -w -Lu",9 );
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
Windowsのデスクトップ画面をそのまま配信するための下準備
WindowsでGPUの状態を確認するには(ASUS系監視ソフトの自動起動を停止する)
CORESERVER v1プランからさくらインターネットスタンダートプランへ引っ越しメモ
さくらインターネットでPython MecabをCGIから使う
さくらインターネットのPHPでAnalytics-G4 APIを使う
インクルードパスの調べ方
【Git】特定ファイルを除外する.gitignore
【Ubuntu/Debian】NVIDIA関係のドライバを自動アップデートさせない
【Python】Spacyを使用して文章から出発地と目的地を抜き出す
【Windows10】リモートデスクトップ間のコピー&ペーストができなくなった場合の対処法
【Apache】サーバーに同時接続可能なクライアント数を調整する
GitLabにHTTPS経由でリポジトリをクローン&読み書きを行う
Windows版Google Driveが使用中と言われアンインストールできない場合
【Linux】iconv/libiconvをソースコードからインストール
【C/C++】小数点以下の切り捨て・切り上げ・四捨五入
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
【PHP】Mail/mimeDecodeを使ってメールの中身を解析(準備編)
Googleファミリーリンクで子供の端末の現在地がエラーで取得できない場合