-->

2015年08月

C言語でやったUnicodeの正規化のC++版だ。C言語の時と同じようにICUを使う。ICUには、C++のAPIも用意されているのでそっちを使う。

こんな感じだ。

#include <stdio.h>
#include <stdlib.h>
#include "unicode/unistr.h"
#include "unicode/normalizer2.h"

void dump(char *mem, int size);

int main()
{
	icu::UnicodeString src("がぎぐげご", "utf-8");
	printf("src.length()=%d\n", src.length());
	dump((char*)src.getBuffer(), src.length() * sizeof(UChar));
	
	// (1) Unicodeの正規化(NFD)の準備
	UErrorCode err = U_ZERO_ERROR;
	const icu::Normalizer2 *normalizer = icu::Normalizer2::getNFDInstance(err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}

	// (2) 正規化する。
	err = U_ZERO_ERROR;
	icu::UnicodeString dest = normalizer->normalize(src, err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	printf("dest.length()=%d\n", dest.length());
	dump((char*)dest.getBuffer(), dest.length() * sizeof(UChar));
	

	return 0;
}


void dump(char *mem, int size)
{
	int addr, offset;
	
	for(addr = 0; addr < size; addr += 16) {
		printf("%04X ", addr & 0xffff);
		for(offset = 0; offset < 16; offset++) {
			if(addr + offset >= size) {
				break;
			}
			printf("%02X ", mem[addr + offset] & 0xff);
		}
		printf("\n");
	}	
}

(追記)
コンパイルは、こうだ。

$ g++ -Wall -o unicode2 `icu-config --cppflags --ldflags` unicode2.cpp

このエントリーをはてなブックマークに追加

と、いってもここにあるHello Worldを試すだけだが。

リンク先に書いてあるように、hello.ccとbinding.gypをコピペして作る。そして、node-gyp configureを実行するんだが、node-gypがないって言われる。(^^;)
node-gypは、npmを使ってインストールできる。こんな感じ。(OS Xの場合)

$ sudo npm install node-gyp -g

node-gyp configureを実行すると、node-v0.12.6.tar.gzを取りにいってる。たしか、Addonで使うC言語のヘッダーファイルとかがなかったと思うんで、それを取得するためと思われる。

そして、node-gyp buildを実行すると、build/Release/addon.nodeができる。
試すためにhello.jsをコピペして作って、node hello.jsで実行する。


このエントリーをはてなブックマークに追加

UnicodeのライブラリICUを使ってUnicodeの正規化を行ってみる。
Unicodeの正規化とは、簡単に言うと見た目が「が」の文字を1つのコード「が」で表す場合と「か」+「゛」(濁点)の2つで表す場合がある。前者を合成文字で後者が結合文字って言う。これらを変換することを正規化っていう。(かなり適当な説明なんで詳しくはググってくれw)

とりあえず、ソースを示す。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include "unicode/ustring.h"
#include "unicode/unorm2.h"

void dump(char *mem, int size);

int main()
{
	char *src = "がぎぐげご";
	UErrorCode err;
	int32_t destLen;
	UChar *dest, *dest2;
	int32_t numSubstitutions;
	
	/* (1) UTF8からUnicodeへの変換後の文字数を取得する。 */
	err = U_ZERO_ERROR;
	u_strFromUTF8WithSub(NULL, 0, &destLen, src, -1, 0xfffd, &numSubstitutions, &err);
	printf("destLen=%d\n", destLen);
	
	/* (2) 変換後の文字列を入れるメモリを確保 */
	dest = (UChar*)calloc(destLen + 1, sizeof(UChar));
	if(!dest) {
		fprintf(stderr, "malloc error\n");
		exit(1);
	}
	
	/* (3) UTF8からUnicodeへ変換 */
	err = U_ZERO_ERROR;
	u_strFromUTF8WithSub(dest, destLen + 1, &destLen, src, -1, 0xfffd, &numSubstitutions, &err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	printf("destLen=%d\n", destLen);	
	dump((char*)dest, (u_strlen(dest) + 1) * sizeof(UChar));

	/* (4) Unicodeの正規化(NFD) */
	err = U_ZERO_ERROR;
	const UNormalizer2 *normalizer = unorm2_getNFDInstance(&err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	/* (5) 正規化後のサイズを取得 */
	err = U_ZERO_ERROR;
	int32_t destLen2 = unorm2_normalize(normalizer, dest, -1, NULL, 0, &err);
	printf("destLen2=%d\n", destLen2);
	
	/* (6) 正規化後の文字列を入れるメモリを確保 */		
	dest2 = (UChar*)calloc(destLen2 + 1, sizeof(UChar));
	if(!dest2) {
		fprintf(stderr, "malloc error\n");
		exit(1);
	}
	/* (7) 正規化する */
	err = U_ZERO_ERROR;
	destLen2 = unorm2_normalize(normalizer, dest, -1, dest2, destLen2 + 1, &err);
	if(U_FAILURE(err)) {
		fprintf(stderr, "err=%d\n", err);
		exit(1);
	}
	dump((char *)dest2, (u_strlen(dest2) + 1) * sizeof(UChar));
	
	free(dest2);
	free(dest);
	
	return 0;
}

void dump(char *mem, int size)
{
	int addr, offset;
	
	for(addr = 0; addr < size; addr += 16) {
		printf("%04X ", addr & 0xffff);
		for(offset = 0; offset < 16; offset++) {
			if(addr + offset >= size) {
				break;
			}
			printf("%02X ", mem[addr + offset] & 0xff);
		}
		printf("\n");
	}	
}

ソースはUTF-8で保存しておいて、コンパイルはこんな感じでする。

$ gcc -o unicode -I/opt/local/include -L/opt/local/lib -licuuc unicode.c

ICUは、MacPortsを使ってインストールしているものとする。

(1) UTF8からUnicodeへ変換した時の文字数を取得。変換できない文字があった場合は0xfffdにして変換するように指定している。また、ICUのAPIで渡すエラーコードが入る変数はU_ZERO_ERRORで初期化しておかないといけない。
(2) メモリ確保。
(3) UTF8からUnicodeへ変換
(4) 正規化の準備。ほんとは、合成文字列への変換を試したかったんだが結合文字列の「がぎぐげご」のUTF8を用意するのが面倒くさかったので、結合文字列の変換するとこにした。合成文字列への変換する場合はunorm2_getNFCInstance()を使う。
(5) 正規化後のサイズ取得。ここでやってる方法はドキュメントに書いてないので注意。
(6) メモリ確保。
(7) 正規化する。

(追記)
icu-configってのが用意されているようなので、コンパイルはこんな感じでする。

$ gcc -Wall -o unicode `icu-config --cflags --ldflags` unicode.c

このエントリーをはてなブックマークに追加

フォームで入力された値を取得してみる。
www/index.htmlにログイン入力フォームを用意する。こんなやつ。

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta charset="utf-8">
	<title>ログイン</title>
</head>
<body>
	<form method="post" action="/post">
	    <p>ユーザID:<input type="text" name="userid"></p>
	    <p>パスワード:<input type="password" name="password"></p>
	    <p><input type="submit"></p>
	</form>
</body>
</html>

userid、passwordをpostメソッドっで/postに送信するようになっている。

次にソース。

/// <reference path="./typings/node/node-0.11.d.ts" />

import http = require('http');
import fs = require('fs');
import url = require('url');
import querystring = require('querystring');

var server = http.createServer(requestServer);
server.listen(9999);

function requestServer(req:http.ServerRequest, res:http.ServerResponse):void {
	var uri = url.parse(req.url, true);		// 第2引数trueでqueryストリングを解析する(getの時使う)
	
	if(uri.pathname == "/post") {
		if(req.method.toLowerCase() === "post") {	// postメソッドなら
			console.log("post");
			var postString = "";
			// 非同期でデータを読み込むようにする。
			req.on("data", data => {
				postString += data;
			});
			// 読み込み終わった場合の処理をする。
			req.on("end", ()=> {
				var postData = querystring.parse(postString);
				res.writeHead(200, { "Content-Type": "text/plain" });
				res.write("userid:" + postData["userid"] + "\n");
				res.write("password:" + postData["password"]);
				res.end();
			});
		} if(req.method.toLowerCase() === "get") {	// getメソッドなら
			// url.parseの第2引数でqueryストリングを解析しているので
			// uri.queryでフォーム入力した値を取得できる。
			console.log("get");
			res.writeHead(200, { "Content-Type":"text/plain"});
			res.write("userid:" + uri.query["userid"] + "\n");
			res.write("password:" + uri.query["password"]);
			res.end();
		} else {
			res.writeHead(404, { "Content-Type": "text/plain" });
			res.write("404 Not Found.");
			res.end();
		}
	} else {
		// ログインフォームのHTMLを返す。
		console.log("etc");
		res.writeHead(200, { "Content-Type": "text/html" });
		fs.createReadStream("www/index.html")
			.pipe(res);
	}
}

ソースのコメントの通りなんで、説明は省く。
いや、ちょっと書いとくか。
req.onで"data"でデータを受信するように設定して、"end"で受信終了時の処理を書く。ここでは、"end"でuseridとpasswordを表示するようにしている。
getメソッドで渡す場合は、queryストリングで渡されるので、url.parseでurlを解析すればいい。



このエントリーをはてなブックマークに追加

[Node.js] XMLファイルの読み込みで書いたようにxml2jsのモジュールと型定義ファイルをインストールしておく。

JavaScriptのオブジェクトを作って、それをファイルに書き込むサンプルは次のようになる。

/// <reference path="./typings/node/node-0.11.d.ts" />
/// <reference path="./typings/xml2js/xml2js.d.ts" />

import fs = require("fs");
import xml2js = require("xml2js");

class test {
	public hoge3:string;
	public hoge4:string;
	
	public constructor(hoge3:string, hoge4:string) {
		this.hoge3 = hoge3;
		this.hoge4 = hoge4;
	}
}

var obj = new test("あいうえお", "かきくけこ");
var builder = new xml2js.Builder();
var xml = builder.buildObject(obj);
fs.writeFile("./test2.xml", xml, err => {
	if(err) {
		console.log(err);
	}
});

実行すると、test2.xmlに保存される。test2.xmlは次のようになる。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
  <hoge3>あいうえお</hoge3>
  <hoge4>かきくけこ</hoge4>
</root>


このエントリーをはてなブックマークに追加

↑このページのトップヘ