# 10. 日本語の取り扱い

- 10.1 漢字を含んだスクリプトが文字化けを出力したり、正しく実行できない場合があります
- 10.2 オプション`-K` と `$KCODE` の違いはなんですか？
- 10.3 日本語の識別子は使えますか
- 10.4 日本語を含む文字列から1文字ずつ文字列を取り出すにはどうしますか
- 10.5 tr("あ","a")がうまく動きません
- 10.6 ひらがなをソートするにはどうしますか
- 10.7 SJISの機種依存コード84BF から 889Fまでを空白に置き換えたいのですが
- 10.8 いわゆる全角文字と半角文字の変換を行うにはどうするのがよいですか？
- 10.9 いわゆる半角カナの扱い
- 10.10 日本語を含む文字列から n バイトを切り出したいのですが
- 10.11 日本語テキストを n 桁で折り返したいのですが
- 10.12 UTF-8で書かれたコードを正しく実行できません

## 10.1 漢字を含んだスクリプトが文字化けを出力したり、正しく実行できない場合があります

ruby 1.6以降、デフォルトで漢字コードを特別に解釈しなくなりました。 漢字を扱いたい場合は `ruby -Ke` などとして$KCODE を適切に設定しておく必要があります。

Windows 上で SJIS を使用している場合、`ruby -Ks`を、 UNIX 系 OS 上で EUC を使用している場合、`ruby -Ke`を UTF-8N を使用している場合、`ruby -Ku`を 指定する必要があります。

なお、スクリプトの先頭行に

    #! ruby -Ks

などと書けばオプションの指定をスクリプトに埋め込むことができます。 これがもっとも無難でポピュラーな方法です。

## 10.2 オプション`-K` と `$KCODE` の違いはなんですか？

効果が及ぶタイミングが違います。

例えばSJISコードのファイルで以下のスクリプトがあった場合

    $KCODE = 'SJIS'
    s = "表"

$KCODE に値を設定する段階ではスクリプトは解析された後です。文字列リテ ラルは($KCODE のデフォルトが "NONE" であるため)マルチバイトとして解釈 されていません。

オプション -K による漢字コードの指定ならばスクリプトを読む前に漢字コー ドが指定されるので、スクリプト解析の段階で漢字コードを認識します。

ちなみに漢字コードを含んだスクリプトで問題が発生する理由は「表」などの 文字がシフトJISコードでバックスラッシュ("\")と同じコードを含むからです。

## 10.3 日本語の識別子は使えますか

-K オプションを正しく指定すれば、漢字で始まる変数名は英小文字に相当するもの として使えます。ポータビリティが低いのでおすすめできません。

Hash を使えば似たようなことができます、こちらの方がより安全と言えます。

    var = {'変数' => '値'}
    var['変数'] = 1

## 10.4 日本語を含む文字列から1文字ずつ文字列を取り出すにはどうしますか

$KCODE を設定した上で split(//) や scan(/./) を使います。

## 10.5 tr("あ","a")がうまく動きません

組み込みの String#tr は、バイトごとに変換します。 `require "jcode"` とすると、日本語を文字ごとに扱えます。 (jcode.rb を参照)

## 10.6 ひらがなをソートするにはどうしますか

以下は、ひらがなの濁音、半濁音、拗音、撥音の文字を無視してソートを行う例です。 *1

    require "jcode"

    a = "ぁぃぅぇぉがぎぐげござじずぜぞだぢづでど" \
        "ばびぶべぼぱぴぷぺぽゃゅょっゎ"
    b = "あいうえおかきくけこさしすせそたちつてと" \
        "はひふへほはひふへほやゆよつわ"

    ary = %w(ふー ばー ばず)

    p ary.sort
    p ary.collect{|l| [l.tr(a,b), l]}.sort.collect!{|e| e[1]}

    # => ["ばー", "ばず", "ふー"]
    #    ["ばー", "ばず", "ふー"]

余談ですが、version 1.7 以降なら Enumerable#sort_by を 使って最後の行を

    p ary.sort_by {|l| l.tr(a,b)}

と書けます。

## 10.7 SJISの機種依存コード84BF から 889Fまでを空白に置き換えたいのですが

正規表現で[あ-ん]というような範囲表現が使えますが、機種依存文字を直接 書くことはプログラムの可読性を損ないます。かといって

    gsub(/[\x84\xbf-\x88\x9f]/s, '　')

と書くことはできません。このような場合は以下のトリックを使うことで

    gsub(Regexp.compile("[\x84\xbf-\x88\x9f]", nil, 's'), '　')
    または
    gsub(/#{"[\x84\xbf-\x88\x9f]"}/s, '　')

2バイトコードの範囲をうまく数値で表現して、空白に置き換えることができ ます。(余談ですが、このようなときには空白でなくゲタ("〓")に置き換えま すけどね)

## 10.8 いわゆる全角文字と半角文字の変換を行うにはどうするのがよいですか？

標準ならば nkf.so ライブラリを使う方法と jcode.rb ライブラ リを使って変換を行う方法があります。あと [RAA:Kakasi] ライブラリな どでも可能です。

[ruby-list:10505], [ruby-list:25839], [ruby-list:31238], [ruby-list:31240], [ruby-list:31508] などなど*2

## 10.9 いわゆる半角カナの扱い

Ruby はいわゆる半角カナを完全にはサポートしてません。

    # 以下の例で "ア" は半角文字としてみてください
    ruby -Ks -e 'p "あア"'
    => "あ\261"

開発中の M17N版 ruby ではこのようなことにはならないそうです。

## 10.10 日本語を含む文字列から n バイトを切り出したいのですが

多バイト文字の切り出しでは、文字の泣き別れが問題になるのです が、これに対しては $KCODE を設定したとき /./ が、分断された 文字にマッチしないことを利用できます。

    $KCODE = "e"
    p /./ =~ "あ"[0,1]    # => nil

    # 注：漢字の要素になり得ない文字コードにはマッチします。

    p /./ =~ "\xff"       # => 0

以下は、文字列の左からたかだか len バイトまでを切り出すメソッ ド jleft のサンプルです。

    class String
      def jleft(len)
        return "" if len <= 0

        str = self[0,len]
        if /.\z/ !~ str
            str[-1,1] = ''
        end
        str
      end
    end

    $KCODE = 'e'
    s = "あいうえお"
    for i in -2 .. s.size+2
        p [i, s.jleft(i)]
    end

    => [-2, ""]
       [-1, ""]
       [0, ""]
       [1, ""]
       [2, "あ"]
       [3, "あ"]
       [4, "あい"]
       [5, "あい"]
       [6, "あいう"]
       [7, "あいう"]
       [8, "あいうえ"]
       [9, "あいうえ"]
       [10, "あいうえお"]
       [11, "あいうえお"]
       [12, "あいうえお"]

もう一つ EUC コード限定で以下のような方法があります。

    class String
      def jleft(len)
        return "" if len <= 0

        str = self[0, len]

        if str.count("\xa1-\xfe") % 2 == 1
          str[-1, 1] = ''
        end
        str
      end
    end

注：いずれも 3 バイト文字には対応してません。

## 10.11 日本語テキストを n 桁で折り返したいのですが

NKF の `-f` オプションが利用できます。

    require 'nkf'
    p NKF.nkf("-ef11", "あいうえお、かきくけこ")

ただ、こちらは禁則処理やスペースの調整などを行ってくれて 賢すぎるので細かく制御できない場合があります。

自力でやるには 10.10 の方法を応用します。

    class String
      def jfold(len)
        return "" if len <= 0

        right = self.delete("\r\n")
        while right and not right.empty?
          left, right = right.unpack("a#{len} a*")

          if /.\z/ !~ left
              right[0,0] = left[-1,1]
              left[-1,1] = ''
          end
          yield left
        end
      end
    end

    "あいうえお、かきくけこ".jfold(11) {|s|
      puts s
    }
    # =>  あいうえお
          、かきくけ
          こ

ただ、これは n バイト毎の folding です。TAB の桁位置を考慮し ていません。また、nkf に比べれば格段に遅いです。

## 10.12 UTF-8で書いたコードを正しく実行できません

10.1 漢字を含んだスクリプトが文字化けを出力したり、正しく実行できない場合があります の項目をチェックしたにもかかわらず、どうしても UTF-8 を正しく解釈してくれない場合は、 UTF-8 と UTF-8N の違いを疑ってみる必要があるかもしれません。

このリファレンスマニュアルでは単に UTF-8 と書かれていますが、 実は ruby インタープリタが解釈するのは UTF-8N です。一般的なエディタでは 暗黙的に UTF-8N のみを取り扱いますが、一部のエディタ*3では UTF-8 と UTF-8N を区別します。このような場合には UTF-8N で保存してみてください。

執筆者募集

*1 あらい 2001-10-07: 例が悪かった(^^;
*2 適当に誰かまとめてちょうだい執筆者募集
*3 RDEで確認。

links: [Enumerable] [FAQ::日本語の取り扱い] [NKF] [String] [jcode.rb] [nkf.so] [執筆者募集] [組み込み変数]
reverse link: [Ruby FAQ]