2024-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 );
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
Wav2Lipのオープンソース版を改造して外部から呼べるAPI化する
Wav2Lipのオープンソース版で静止画の口元のみを動かして喋らせる
【iOS】アプリアイコン・ロゴ画像の作成・設定方法
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【2】
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【1】
【Xcode】iPhone is not available because it is unpairedの対処法
【Let's Encrypt】Failed authorization procedure 503の対処法
【Debian】古いバージョンでapt updateしたら404 not foundでエラーになる場合
ファイアウォール内部のWindows11 PCにmacOS Sequoiaからリモートデスクトップする
進研ゼミチャレンジタッチをAndroid端末化する
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
Windows11のコマンドプロンプトでテキストをコピーする
Androidホームで左にスワイプすると出てくるニュース共を一切表示させない方法
【Apache】サーバーに同時接続可能なクライアント数を調整する
Googleスプレッドシートを編集したら自動で更新日時を入れる
VirtualBoxの仮想マシンをWindows起動時に自動起動し終了時に自動サスペンドする
Node.jsからPostgreSQLサーバに接続・操作する
【Makefile】依存関係ファイル/静的/動的ライブラリ/実行ファイルをまとめてメイク