メインコンテンツ | メニュー

Main Contents

2008年5月 5日

GW 中にやったこと

GW 中にやったことをちょっとまとめてみる...
インフラまわりは, ほとんど深夜やっていたので, 寝るのは午前中って感じでした...
でも, かなりすっきり.

確定申告

毎年のことなのですが, うちの会社の「確定深刻」は GW 勝負なのです(汗)

今年は, e-Tax を使ってみましたが, イマイチ便利になってない...
税務署へ持って行く手間はなくなりましたが, 事業税などの申告は別にやらなきゃいけないし, 電子証明書の申請をしに法務局へ行かなきゃいけない.
数字の計算くらい, 明細書から自動計算してくれれば楽なのに(苦笑)

FreeBSD 4.x-RELEASE から 6.3-RELEASE へのアップグレード

FreeBSD 4.11-RELEASE が, まだ4台稼動していたので, すべて 6.3-RELEASE にアップグレード.
Celeron 700MHz という骨董品ですが, これでまた, 暫くは心おきなく使えますw

手順は, 下記とほとんど同じ
FreeBSD4 から FreeBSD6 へのアップグレード

jail でまとめる

上記の FreeBSD4.11-RELEASE で稼動していたうちの3台を, jail(8) を使って1台に統合しました.
減らしたやつは, 予備機として頑張ってもらいます.

sysutils/ezjail を使って構築も楽ちん.

ipfilter から pf へ乗り替え

ipfilter は, 今後メンテされなくなるようなので, 6.3-RELEASE へアップグレードしたついでに, pf へ乗り替え.
構文もすっきりして, メンテもしやすくなりました.
特に, ルータとして使っているやつは, 細かい設定ができてステキ.
set block-policy drop とか良い感じです.

下記を参考にしました
FreeBSDでPacketFilter(pf)を使う

過去の遺産の整理...

4.11-RELEASE が現在までも稼動していたのもそうですが, 過去に書いたバックアップスクリプトとかを, メンテしやすいようにいろいろ更新. サーバーで USBキーボードを使えるようにしたりだとか...
あ, DHCP サーバーも jail(8) で動くようにして, WIDE-DHCP を net/isc-dhcpd-server にしました.

Firemacs のアイコン

firemacs.jpg

山本さん, 大変永らくお待たせ致しました...(汗)
また, 拙作のアイコンをご採用頂き, ありがとうございました!

心残り...

EC-CUBE をほとんど触れなかったのがざんねん...
もうちょっと精進します...

Apple Store(Japan)

2008年4月27日

NTEmacs にしてみた

今まで, Windows 環境では Meadow を使っていましたが, NTEmacs もちょっと気になって NTEmacsJp project の NTEmacs を入れてみました.

そしたら, アイコンが Emacs23 と同タイプになっているではありませんか!!
感謝感激!! 廣松さん, ありがとうございます!

使い心地は, 良い感じ.
気のせいかもしれませんが, Meadow に比べると, ちょっと速くなったような感じがします.
でも, Meadow みたいに NetInstall で簡単に設定することができないので, 玄人向けですね.

しかし, Windows 環境でも, Emacs23 のアイコンが使えてシアワセです.

  • by みそ
  • at 02:29
  • in Emacs

2008年4月21日

BeanUtils の使い方

かなり前に書いた記事を, ちゃんとまとめてみました.

BeanUtils の使い方サンプルです.
JUnit4 のテストケースとして書いてみました.

/*
 * $Id$ 
 */
package net.nanasess.examples;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.junit.Before;
import org.junit.Test;

/**
 * BeanUtils の使い方.
 * 
 * 基本的に, データ型を String に変換して返します.
 * 変換したくない場合は, {@link PropertyUtils} を使います. 
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class BeanUtilsTest {
    
    private FooBar fooBar;
    
    @Before
    public void before() {
        fooBar = new FooBar();
    }
    
    /**
     * {@link BeanUtils#cloneBean(java.lang.Object)} の使い方.
     * 
     * Cloneable を implements してなくても Bean の複製ができます.
     */
    @Test
    public final void testCloneBean() throws Exception {
        Bar bar = new Bar("bar");
        String excepted = "bar";
        
        // bar を複製する
        Bar actual = (Bar) BeanUtils.cloneBean(bar);
        
        assertEquals(excepted, actual.getExample());
    }

    /**
     * {@link BeanUtils#copyProperties(java.lang.Object, java.lang.Object)} の使い方
     * 
     * 同じプロパティ名のデータを orig から dest へコピーします.
     * この場合, example というプロパティが双方に存在するので, example のデータがコピーされます.
     */
    @Test
    public final void testCopyProperties() throws Exception {
        Bar orig = new Bar();
        ExampleClass dest = new ExampleClass();
        
        // 同じプロパティ名のデータを orig から dest へコピーする.
        BeanUtils.copyProperties(dest, orig);
        
        // orig が Map の場合は, プロパティ名と Map の key が一致した場合コピーされる.
        Map map = new HashMap();
        map.put("foo", "foo");
        BeanUtils.copyProperties(dest, map);
        
        // example の内容がコピーされる
        assertEquals(orig.getExample(), dest.getExample());
        // Map の内容もコピーされる
        assertEquals(map.get("foo"), dest.getFoo());
        // baz は null のまま 
        assertNull(dest.getBaz());
    }

    /**
     * {@link BeanUtils#copyProperty(java.lang.Object, java.lang.String, java.lang.Object)} の使い方.
     * 
     * 指定したプロパティのデータをコピーします.
     * ネストしたプロパティもコピーできます.
     */
    @Test
    public final void testCopyProperty() throws Exception {
        Bar bar = new Bar();
        ExampleClass bean = new ExampleClass();
        
        // 指定したプロパティのデータを bean にコピーします.
        BeanUtils.copyProperty(bean, "foo", "foo");
        // ネストした Bean もコピーできます.
        BeanUtils.copyProperty(bean, "bar", bar);
        
        assertEquals("foo", bean.getFoo());
        assertEquals("example", bean.getBar().getExample());
        
    }

    /**
     * {@link BeanUtils#describe(java.lang.Object)} の使い方.
     * 
     * bean のプロパティ名を key, プロパティのデータを value とした Map を返します.
     * プロパティのデータ型に関わらず, value は String になります...
     */
    @Test
    public final void testDescribe() throws Exception {
        
        // value に格納されるデータを String にキャストして Map で返します.
        Map map = BeanUtils.describe(fooBar);
        
        assertEquals("foo", map.get("foo"));
    }

    /**
     * {@link BeanUtils#getArrayProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名を指定して, プロパティの値を String の配列で返します.
     * プロパティのデータ型が配列の場合は, String の配列に変換して返します.
     * コレクションの場合も, String の配列に変換して返します.
     * それ以外のデータ型の場合は, {@link Object#toString()} の値を String の配列に格納して返します.
     */
    @Test
    public final void testGetArrayProperty() throws Exception {
        
        // プロパティ名を指定して, String の配列を返します.
        String[] strings = BeanUtils.getArrayProperty(fooBar, "strings");
        assertArrayEquals(fooBar.getStrings(), strings);
        
        // プロパティが配列では無い場合も String の配列が返ってきます.
        String[] foo = BeanUtils.getArrayProperty(fooBar, "foo");
        assertArrayEquals(new String[]{"foo"}, foo);
    }

    /**
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名[インデックス番号] のように, インデックスを保持するプロパティから, インデックス番号を指定して
     * 値を String で返します.
     * インデックスを保持しないプロパティは, {@link IllegalArgumentException} になります.
     */
    @Test(expected=IllegalArgumentException.class)
    public final void testGetIndexedPropertyObjectString() throws Exception {
        
        // プロパティ名とインデックス番号を指定して String の値を返します.
        String actual = BeanUtils.getIndexedProperty(fooBar, "strings[2]");
        assertEquals("ghi", actual);
        
        // インデックスを保持しないプロパティは IllegalArgumentException になります.
        String foo = BeanUtils.getIndexedProperty(fooBar, "foo[0]");
    }

    /**
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String, int)} の使い方.
     * 
     * {@link BeanUtils#getIndexedProperty(java.lang.Object, java.lang.String)} に
     * 似ていますが, インデックス番号を int で指定するところが異なります.
     */
    @Test
    public final void testGetIndexedPropertyObjectStringInt() throws Exception {
        
        // プロパティ名とインデックス番号を指定して, String の値を取得します.
        String actual = BeanUtils.getIndexedProperty(fooBar, "strings", 2);
        assertEquals("ghi", actual);
    }

    /**
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティが Map の場合, プロパティ名(key) のように指定して, 値を String で返します.
     * プロパティが Map 以外の場合は, null を返します.
     */
    @Test
    public final void testGetMappedPropertyObjectString() throws Exception {
        
        // プロパティ名と key を指定して, String の値を返します. 
        String actual = BeanUtils.getMappedProperty(fooBar, "map(abc)");
        assertEquals("abc of value", actual);
        
        // プロパティが Map 以外の場合は, null を返します. 
        assertNull(BeanUtils.getMappedProperty(fooBar, "foo(key)"));
    }

    /**
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String, java.lang.String)} の使い方.
     * 
     * {@link BeanUtils#getMappedProperty(java.lang.Object, java.lang.String)} に
     * 似ていますが, key を第3引数で指定するところが異なります.
     */
    @Test
    public final void testGetMappedPropertyObjectStringString() throws Exception {
        
        // プロパティ名と key を指定して, String の値を返します.
        String actual = BeanUtils.getMappedProperty(fooBar, "map", "abc");
        assertEquals("abc of value", actual);
    }

    /**
     * {@link BeanUtils#getNestedProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * ネストした Bean のプロパティをドットシンタックスで取得できます.
     * 値は String で返します.
     */
    @Test
    public final void testGetNestedProperty() throws Exception {
        
        // ネストしたプロパティを取得します.
        String actual = BeanUtils.getNestedProperty(fooBar, "bar.example");
        assertEquals("example", actual);
    }

    /**
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * 便利屋さんです.
     * プロパティのデータ型に合わせた方法で, プロパティ名やインデックスを指定することで, プロパティの値を
     * String で取得できます.
     */
    @Test
    public final void testGetProperty() throws Exception {
        
        // プロパティ名を指定して String の値を返します.
        String foo = BeanUtils.getProperty(fooBar, "foo");
        assertEquals("foo", foo);
        
        // プロパティ名とインデックス番号を指定して String の値を返します.
        String strings = BeanUtils.getProperty(fooBar, "strings[2]");
        assertEquals("ghi", strings);
        
        // プロパティ名と key を指定して, String の値を返します. 
        String map = BeanUtils.getProperty(fooBar, "map(abc)");
        assertEquals("abc of value", map);
        
        // ネストしたプロパティを取得します.
        String actual = BeanUtils.getProperty(fooBar, "bar.example");
        assertEquals("example", actual);
    }

    /**
     * {@link BeanUtils#getSimpleProperty(java.lang.Object, java.lang.String)} の使い方.
     * 
     * プロパティ名を指定して, String の値を返します.
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} とは挙動が
     * 異なるため, 単純な Bean の値を取得するのに適しています.
     */
    @Test(expected=IllegalArgumentException.class)
    public final void testGetSimpleProperty() throws Exception {
        
        // プロパティ名を指定して String の値を返します.
        String foo = BeanUtils.getSimpleProperty(fooBar, "foo");
        assertEquals("foo", foo);
        
        // プロパティが配列の場合は先頭の値を返します.
        String strings = BeanUtils.getSimpleProperty(fooBar, "strings");
        assertEquals("abc", strings);
        
        // プロパティが Map の場合はMap#toString() の値を返します. 
        String map = BeanUtils.getSimpleProperty(fooBar, "map");
        assertEquals("{abc=abc of value, ghi=ghi of value, def=def of value}", map);
        
        // ネストしたプロパティは取得できません...
        try {
            BeanUtils.getSimpleProperty(fooBar, "bar.example");
        } catch (IllegalArgumentException e) {
            throw e;
        }
    }

    /**
     * {@link BeanUtils#populate(java.lang.Object, java.util.Map)} の使い方.
     * 
     * Map に格納した値を key に対応する Bean のプロパティに設定します.
     * 値は, String にキャストして設定します.
     */
    @Test
    public final void testPopulate() throws Exception {
        Map map = new HashMap();
        map.put("foo", "foo of value");
        map.put("bar.example", new Integer(1000));
        
        // Map の key に対応するプロパティに値を設定します.
        BeanUtils.populate(fooBar, map);
        assertEquals("foo of value", fooBar.getFoo());
        
        // Integer などの値も String にキャストされます.
        assertEquals("1000", fooBar.getBar().getExample());
    }

    /**
     * {@link BeanUtils#setProperty(java.lang.Object, java.lang.String, java.lang.Object)} の使い方.
     * 
     * {@link BeanUtils#getProperty(java.lang.Object, java.lang.String)} と同様の方法で,
     * プロパティ名を指定することで, 任意のオブジェクトを String にキャストして Bean に設定します.
     */
    @Test
    public final void testSetProperty() throws Exception {
        
        // プロパティ名を指定して String の値を設定します.
        BeanUtils.setProperty(fooBar, "foo", "foo of value");
        assertEquals("foo of value", fooBar.getFoo());
        
        // プロパティ名とインデックス番号を指定して String の値を設定します.
        BeanUtils.setProperty(fooBar, "strings[2]", "strings of two");
        String[] strings = fooBar.getStrings();
        assertEquals("strings of two", strings[2]);
        
        // プロパティ名と key を指定して, String の値を設定します. 
        BeanUtils.setProperty(fooBar, "map(abc)", new Integer(1000));
        assertEquals("1000", fooBar.getMap().get("abc"));
        
        // ネストしたプロパティを設定します.
        BeanUtils.setProperty(fooBar, "bar.example", "bar of example");
        assertEquals("bar of example", fooBar.getBar().getExample());
    }
}

Bean は下記のものを使っています.

FooBar.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * FooBar クラス.
 * 
 * 内部で Bar クラスをネストしている.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class FooBar {

    private String foo;
    private Bar bar;
    private String[] strings;
    private List bars;
    private Map map;
    
    public FooBar() {
        this.foo = "foo";
        bar = new Bar();
        strings = new String[]{"abc", "def", "ghi"};
        bars = new ArrayList();
        map = new HashMap();
        for (String string : strings) {
            bars.add(new Bar(string));
            map.put(string, string + " of value");
        }
    }
    
    public String getFoo() {
        return foo;
    }
    public void setFoo(String foo) {
        this.foo = foo;
    }
    public Bar getBar() {
        return bar;
    }
    public void setBar(Bar bar) {
        this.bar = bar;
    }
    public String[] getStrings() {
        return strings;
    }
    public void setStrings(String[] strings) {
        this.strings = strings;
    }
    public List getBars() {
        return bars;
    }
    public void setBars(List bars) {
        this.bars = bars;
    }
    public Map getMap() {
        return map;
    }
    public void setMap(Map map) {
        this.map = map;
    }
}

Bar.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.sql.Timestamp;

/**
 * Bar クラス.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class Bar {

    private String example;
    private Timestamp created;

    public Bar() {
        this.example = "example";
        this.created = new Timestamp(System.currentTimeMillis());
    }
    
    public Bar(String example) {
        this.example = example;
        this.created = new Timestamp(System.currentTimeMillis());
    }
    
    public String getExample() {
        return example;
    }

    public void setExample(String example) {
        this.example = example;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }
}

ExampleClass.java
/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.sql.Timestamp;

/**
 * ExampleClass クラス.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class ExampleClass {
    
    private String foo;
    private String baz;
    private String example;
    private Bar bar;
    private Timestamp created;

    public String getFoo() {
        return foo;
    }

    public void setFoo(String foo) {
        this.foo = foo;
    }

    public String getBaz() {
        return baz;
    }

    public void setBaz(String baz) {
        this.baz = baz;
    }
    
    public String getExample() {
        return example;
    }
    
    public void setExample(String example) {
        this.example = example;
    }

    public Bar getBar() {
        return bar;
    }

    public void setBar(Bar bar) {
        this.bar = bar;
    }

    public Timestamp getCreated() {
        return created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }
}

以前の記事にも書いたのですが, BeanUtils は, 基本的に値を String にキャストします.
PropertyUtils は, 型変換せずに扱います.
それ以外の振舞いは, 基本的に同じです.

これらの API を使いこなすと非常に強力ですが, 最近はフレームワークがサポートしている場合が多いようです.
(Seasar の S2BeanUtils など)

今回ひさびさに, BeanUtils のソースを読んだんですが, やはりリフレクションの良いお手本です.

PropertyUtils のサンプルコードは, また後日...

BeanUtils の使い方の続きを読む

  • by みそ
  • at 01:47
  • in Java

2008年4月20日

JUnit4 ことはじめ

ずっと食わず嫌いだった JUnit4 を使ってみたので, コードを晒しておきます...

/*
 * $Id$ 
 */
package net.nanasess.examples;

import java.util.ArrayList;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;

/**
 * HelloWorld のテスト.
 * 
 * @author Kentaro Ohkouchi
 * @version $Revision$ $Date$
 */
public class HelloWorldTest {

    private static String expected;
    private String actual;
    private static int counter;    

    /**
     * すべてのテスト前に実行するメソッド.
     * 
     * static にして {@link @beforeClass} アノテーションをつける.
     */
    @BeforeClass
    public static void beforeClass() {
        System.out.println("HelloWorld テストを始めます.");
        counter = 0;
        System.out.println("カウンターを初期化しました.");
        
    }
    
    /**
     * すべてのテストを終了した後に実行するメソッド.
     * 
     * static にして {@link @AfterClass} アノテーションをつける.
     */
    @AfterClass
    public static void afterClass() {
        System.out.println(counter + " 個のテストを実行しました.");
        System.out.println("HelloWorld テストを終了します.");
    }
    
    /**
     * 各テストをする前に実行するメソッド.
     * 
     * {@link @Before} アノテーションをつける.
     */
    @Before
    public void before(){
        expected = "HelloWorld";
        System.out.println("expected を初期化しました.");
    }
    
    /**
     * 各テストの後に実行するメソッド.
     * 
     * {@link @After} アノテーションをつける. 
     */
    @After
    public void after() {
        counter++;
        System.out.println(String.valueOf(counter) + " 番目のテストを実行しました.");
    }

    /**
     * HelloWorld のテスト.
     */
    @Test
    public void helloWorld() {
        actual = "HelloWorld";
        assertEquals(expected, actual);
    }

    /**
     * ちゃんと ClassNotFoundException になるかどうかのテスト.
     * 
     * @throws Exception エラーが発生した場合.
     */
    @Test(expected = ClassNotFoundException.class)
    public void classForNameFailure() throws Exception {
        Class.forName("org.postgresql.Driver");
    }

    /**
     * スタブなので, スキップするテスト.
     * 
     * {@link @Ignore} アノテーションをつけると, そのメソッドはテストしない.
     */
    @Ignore("スタブなのでテストしません...")
    @Test
    public void testStub() {
        assertEquals("test", "stub");
    }

    /**
     * 配列を比較するテスト.
     * 
     * {@link org.junit.Assert#assertArrayEquals(String[], String[])} を使ってます.
     * 配列や, List のテストも楽になったものです(しみじみ)
     */
    @Test
    public void arrays() {
        String[] expecteds = { "1", "2", "3" };
        List actuals = new ArrayList();
        actuals.add("1");
        actuals.add("2");
        actuals.add("3");

        assertArrayEquals(expecteds, actuals.toArray(new String[actuals.size()]));
    }
}

個人的には, 配列や例外のテストが解りやすく楽になったのが良い感じ.
JUnit4 の関連ライブラリも充実してきたので, そろそろ乗り替え時かな...

ついでに, Eclipse 3.4M6a を Celeron 600MHz, メモリ 384MB という非力なノートPCで使ってみましたが, 思ったよりサクサクで良い感じでした☆

  • by みそ
  • at 16:05
  • in Java

2008年4月16日

sitemap.xml

MT4 で サイトマップ(sitemap.xml)を作成 を参考に, sitemap.xml も作ってみました.

FastCGI といい, こうやって細かいチューニングができるのは, 自前サーバーの強みですね...

MovableType を FastCGI で動作させる

最近, スパムコメントやスパムトラックバックで, サーバーの負荷が酷な状態になっていたため, 負荷低減のために MovableType を FastCGI で動作するようにしてみました.

参考 http://wiki.movabletype.org/Hosting_MT_under_FastCGI_(Japanese)

  1. まず, 必要なライブラリをインストール. FreeBSD なので ports から...
    # portinstall www/p5-FastCGI
    # portinstall www/mod_fastcgi
           
    portupgrade を使わない場合は, /usr/ports/www/p5-FastCGI, /usr/ports/www/mod_fastcgi で make install で OK
  2. httpd.conf に以下を追加
    LoadModule fastcgi_module     libexec/apache2/mod_fastcgi.so
    
    <IfModule fastcgi_module>
        FastCgiIpcDir /var/tmp/fcgi_ipc/
        AddHandler fastcgi-script fcgi
        FastCGIConfig -autoUpdate -idle-timeout 120 -killInterval 3600 -maxClassProcesses 6 -maxProcesses 15
    </IfModule>
    
  3. mod_fastcgi を有効にするため, apache を再起動
    # apachectl restart
    
  4. MovableType の mt*.cgi を mt*.fcgi にリネーム. とりあえず mt*.cgi も動くようにコピーしました.
    $ cp mt.cgi mt.fcgi
    $ cp mt-tb.cgi mt-tb.cgi
    $ cp mt-comments.cgi mt-comments.fcgi
    $ cp mt-search.cgi mt-search.fcgi
    $ cp mt-view.cgi mt-view.fcgi
    
  5. mt-config.cgi の編集. とりあえず必要な箇所のみ *.fcgi にしました.
     AdminScript mt.fcgi
     CommentScript mt-comments.fcgi
     TrackbackScript mt-tb.fcgi
     SearchScript mt-search.fcgi
    # XMLRPCScript mt-xmlrpc.pl
     ViewScript mt-view.fcgi
    # AtomScript mt-atom.pl
    # UpgradeScript mt-upgrade.pl
    

あとは, mt.fcgi にアクセスして, FastCGI で動作することを確認し, サイトをすべて再構築してやれば OK です.

これで負荷が減ってくれれば良いのですが...

MovableType を FastCGI で動作させるの続きを読む

2008年4月11日

JIS X 0213:2004(JIS2004) と Java と PHP と RDBMS

ずいぶん前に, EC-CUBE の ML へ投稿した話題ですが, どうも過去ログに埋もれているので, 再度まとめてみます...

Windows Vista で, 日本語の文字集合に, JIS X 0213:2004(JIS2004) が採用されました.
このため, 扱える漢字が増えたり, 字体が変化したりして, さまざまな影響があります.

EC-CUBE を代表する PHP のアプリケーションや, Java の場合, どのような影響が考えられるでしょうか.

以前, Windows Vista での影響調査をする機会があり, いろいろ調べましたので, ちょっとまとめてみます.

文字集合と文字エンコーディングについて

文字のまとまりを規格化できるよう, 各文字に対して一連のコード(文字コード)を割り当てたものを「文字集合(文字セット)」と言います.
これには, Unicode や, JISX 0213:2004 などがあたります.

この文字集合をコンピュータで扱えるよう, 文字コードをバイトコードへ変換する仕組み(文字符号化方式)を文字エンコーディングと言います.
これには, Shift_JIS や, EUC-JP, UTF-8, UTF-16 などがあります.

文字コードと文字エンコーディングと文字セットを混同してしまいがちですが, それぞれの意味は別物です.

UTF-8 というのは, 正確に言うと Unicode という文字セットで規定されている文字エンコーディングの一つです.

Unicode で規定されている文字エンコーディングの抜粋

  • UTF-1 - 8bit
  • UTF-5 - 利用されていない
  • UTF-7 - UTF-16 で表した Unicode を Base64 で変換して表す方式. 非推奨
  • UTF-8 - 8bit 単位の可変長コードにエンコードする方式
    • 1バイト - すべての ASCII
    • 2バイト - JIS X 0208の非漢字の一部
    • 3バイト - Windows-31J の拡張文字エリア全て, 第3・4水準漢字の一部
    • 4バイト - JIS X 0213:2004 の第3・4水準漢字の一部, UnicodeのBMP 面以外すべて
  • UTF-16 - Windows XP, Vista の内部エンコーディング. Vista では, JIS X 0213:2004 の文字集合が採用された. 16bit 単位の可変長. 16bit のみで表現される BMP面と, 16bit + 16bit で表現される「サロゲート・ペア」から構成される.
  • UTF-32 - Unicode の全コードを単一長のコードとして 32bit で指定するコード. 実際に使用されるのは 21bit までの範囲.

Unicode における BMP(Basic Multilingual Plane)面とは, UTF-16 にて, 16bit のみで表現できる領域の文字のことを言い, 16bit + 16bit で表現する文字の範囲を 「サロゲート・ペア」と言います.

Unicode 以外の文字集合としては, UCS-4 (ISO/IEC 1064)という良く似た別物があり, UCS-4 で規定されている文字エンコーディングにも UTF-8 があったりします.

UCS-4 と Unicode は 21bit までの範囲で互換性があります. UCS-4 は4バイトを基本とし, 31bit までの領域を使用します. ややこしい...

一方, JIS X 0213:2004 では, 文字エンコーディングとして, ISO-2202-JP-2004, EUC-JP-2004, Shift_JIS-2004 などがありますが, IANA に登録されていないため, あくまでも参考となっています.

Windows Vista での問題

Windows Vista では, 使用する文字セットに, JIS X 0213:2004 を採用したため, UTF-16 でエンコーディングされる場合, BMP面以外の文字(16bit + 16bit で表現される文字)までサポートされることとなりました.

このため, UTF-8 でエンコーディングされる場合は, 1文字が4バイトで表現される文字も含まれます.

PHPでの扱い

http://jp.php.net/mbstring を見るかぎりは, mbstring 関数を使用することで, サロゲート・ペアの文字が入力されても, うまく対応できそうです.
文字エンコーディングは, UTF-8 が無難でしょう.

MySQL での問題

http://www.mysql.gr.jp/mysqlml/mysql/msg/13823
これを見ると, DBエンコーディングに UTF-8 を使用した場合でも, 扱えるのは, 1文字3バイトまでの範囲に限られるようです...

つまり, Windows Vista で採用されたサロゲート・ペアの文字が入力された場合は, 確実に文字化けします(苦笑)

PostgreSQL での扱い

8.1.7以降、8.2.2以降であれば, UTF-8 で問題なく扱えるようです.
それ以前のバージョンは, 1文字4バイトで表現される文字を使用した場合に問題がでる可能性あります.

http://mlog.euqset.org/archives/pgsql-jp.ml.postgresql.jp/38404.html

Oracle は?

Oracle は 10.1.0以降で対応とのことです.

Java での問題

char が 16bit であるため, 16bit + 16bit のサロゲート・ペアは正常に扱えません...

具体的には, String#length() や, String#substring() が正常に動作しません(苦笑)

プロパティファイルなど, \uXXXX で表わすものは 16bit なので, サロゲート・ペアの文字は使えません.
プロパティファイルを XML で書けば, 任意のエンコーディングを使用できるので, サロゲート・ペアの文字も使用できます.

Java でサロゲート・ペアの文字列を扱うには, Java SE 5.0 以降で追加された API を使います.

サロゲート・ペアが含まれる文字数を正確に数えるには, String#codePointCount() を使ったりします.

文字列のソートとかも工夫が必要です. 手前味噌ですが, 下記にサンプルがあります.
http://d.hatena.ne.jp/nanasess/20070819/1187700166

さらに...

http://itpro.nikkeibp.co.jp/99/vista/index.html
これを見ると, Vista で入力できても, XP 以前では対応していない文字が183字あり, この中には人名漢字も含まれます.

仮にWebアプリケーション側が Vista にフル対応したとしても, XP では表示できないというトラブルも発生します.

XP に JIS2004対応フォントをインストールしたりすることで回避できますが, 対応をクライアント側に任せるのもどうかと...

いちばん良いのは, Vista で問題の文字を入力した場合に警告を出すことでしょうかねえ...

参考

J2SE 5.0 Tiger 虎の穴 Unicode 4.0 の補助文字のサポート

Java プラットフォームにおける補助文字のサポート

JIS X 0213:2004(JIS2004) と Java と PHP と RDBMSの続きを読む

  • by みそ
  • at 01:01
  • in Java

Copyright © nanasess.net.Creative Commons LicenseThis weblog is licensed under a Creative Commons License.