Ruby Ruby M17NM17N― ― RubyKaigiRubyKaigi’’0808 版 版 ――
成瀬ゆい成瀬ゆいMartin J. DMartin J. Düürstrst
SummarySummary概論 概論 (intro) (intro) RubyRuby の場合 の場合 (Ruby(Ruby’’s s Case) Case)
変換 変換 (transcoding) (transcoding) by by MartinMartin
質疑応答 質疑応答 (questions)(questions)
Who is naruseWho is naruse
最年少コミッタ最年少コミッタ nkfnkf Softbank TechnologySoftbank Technology
– iPhoneiPhone とかとか [email protected]@ruby-lang.org
Ruby M17N Ruby M17N の特の特徴徴
CSI CSI 方式を採用方式を採用独自の変換モジュー独自の変換モジュールル
多言語化の手法多言語化の手法M17N methods:M17N methods:UCS Normalization UCS Normalization 方方式式
CSICSI 方式方式 (Code Set (Code Set Independent)Independent)
UCS Normalization UCS Normalization 方方式式
文字の内部表現を一つの文字の内部表現を一つの UCS UCS (Universal Character Set)(Universal Character Set) で統で統一一
文字列の入力時にデコード文字列の入力時にデコード出力時にエンコード出力時にエンコード他のほとんどの言語で採用他のほとんどの言語で採用
Perl's case (Unicode)Perl's case (Unicode)
Decode:Decode:
$str = decode("UTF-8", "\xE3\x81\$str = decode("UTF-8", "\xE3\x81\x82");x82");
$str → "$str → " ああ ""
Encode:Encode:
$bytes = encode("UTF-8", "$bytes = encode("UTF-8", " ああ ");");
$bytes → "\xE3\x81\x82"$bytes → "\xE3\x81\x82"
CSI CSI 方式方式 Code Set IndependentCode Set Independent 唯一特別なエンコーディングを持た唯一特別なエンコーディングを持た
ないない 不必要な変換は行わなくてよい不必要な変換は行わなくてよい Solaris, CitrusSolaris, Citrus 他に 他に __STDC_ISO_10646__ __STDC_ISO_10646__ でない でない
CC
RubyRuby の場合の場合String String がエンコーディングを持がエンコーディングを持
つつ中身はバイト列のまま中身はバイト列のまま"" あいうえおあいうえお ".encoding".encoding
-> <Encoding:UTF-8>-> <Encoding:UTF-8>
3 Encoding 3 Encoding GradesGrades
ASCII CompatibleASCII CompatibleASCII IncompatibleASCII IncompatibleDummyDummy
ASCII CompatibleASCII Compatiblefull supportfull supportscript encodingscript encodingfasterfasterUTF-8, Shift_JIS, EUC-JP, ...UTF-8, Shift_JIS, EUC-JP, ...
Major EncodingsMajor Encodings
US-ASCIIUS-ASCIIASCII-8BITASCII-8BITUTF-8UTF-8
Japanese Japanese EncodingsEncodings
Shift_JISShift_JISEUC-JPEUC-JP
Other Other EncodingsEncodings
Big5, EUC-KR, EUC-TW, GBK,Big5, EUC-KR, EUC-TW, GBK,
ISO-8859-X, KOI8-R, KOI8-U, ISO-8859-X, KOI8-R, KOI8-U, etcetc
Machine dependend Machine dependend EncodingsEncodings
Windows-31JWindows-31JCP51932CP51932eucJP-mseucJP-msWindows-125XWindows-125X
ASCII-8BITASCII-8BIT
ASCII Compatible 8BIT ASCII Compatible 8BIT StringString
BINARY?BINARY?
ASCII OnlyASCII Only7BIT String is special7BIT String is special"abcde".ascii_only? -> "abcde".ascii_only? -> truetrue
"abcde" + ""abcde" + " あいうえおあいうえお ""
ASCII IncompatibleASCII Incompatible
limited supportlimited supportCanCan’’t use as script t use as script encodingencoding
UTF-{16,32}{BE,LE}UTF-{16,32}{BE,LE}
UTF-16 & UTF-32UTF-16 & UTF-32UTF-16BE, UTF-16LEUTF-16BE, UTF-16LEUTF-32BE, UTF-32LEUTF-32BE, UTF-32LEしかし、しかし、 UTF-16 UTF-16 や や UTF-UTF-32 32 には非対応には非対応
Dummy encodingDummy encodingRuby Ruby は名前を知っているだは名前を知っているだ
けけ「文字」のサポートはしない「文字」のサポートはしないfor stateful encodingsfor stateful encodingsEncoding#dummy? -> trueEncoding#dummy? -> trueISO-2022-JP, UTF-7ISO-2022-JP, UTF-7
Encoding.listEncoding.listEncoding.listEncoding.list
→ → [encoding, ..][encoding, ..]Encoding.name_listEncoding.name_list
→ → [enc_name, ..][enc_name, ..]Encoding.aliasesEncoding.aliases
→ → {alias => enc_name, ..}{alias => enc_name, ..}
$KCODE is obsolete$KCODE is obsolete
$KCODE$KCODE は内部コード指定のため使われは内部コード指定のため使われたた
Ruby1.9Ruby1.9 ではシステム全体の内部コードではシステム全体の内部コードは、は、
決定不可能なため、廃止。決定不可能なため、廃止。 $KCODE$KCODE を参照しているスクリプトは注を参照しているスクリプトは注
意意
StringString
1.8: Byte String1.8: Byte String– Ruby ignores encodingRuby ignores encoding
1.9: Byte String with encoding1.9: Byte String with encoding– Ruby knows the encoding of Ruby knows the encoding of
stringstring
No Character No Character ObjectObject
but 1 Character Stringbut 1 Character String
?? ああ .class -> String.class -> String
Why?Why?
A character has A character has ......codepointcodepoint
encodingencodingbyte stringbyte string1 char string has them!1 char string has them!cf.cf. 大クラス主義大クラス主義
文字リテラル文字リテラル
1.8:1.8: ?a?a
→ → 97 (Fixnum)97 (Fixnum) ?\x61?\x61
→ → 97 (Fixnum)97 (Fixnum)
1.9:1.9: ?a?a
→ → "a" (US-ASCII)"a" (US-ASCII) ?\x61?\x61
→ → "a" (US-ASCII)"a" (US-ASCII) ?? ああ
→ → "" ああ " (UTF-8)" (UTF-8) ?\u{3042}?\u{3042}
→ → "" ああ " (UTF-8)" (UTF-8)
String#ord and Integer#chrString#ord and Integer#chr
"" ああ ".ord → 12354 # Unicode".ord → 12354 # Unicode 12354.chr12354.chr
→ → RangeError: 12354 out of char RangeError: 12354 out of char rangerange
12354.chr("UTF-8")12354.chr("UTF-8")
→ → "" ああ ""
文字列のエンコーディン文字列のエンコーディンググ
?a.encoding → <Encoding:US-ASCII>?a.encoding → <Encoding:US-ASCII> "a".encoding → <Encoding:US-ASCII>"a".encoding → <Encoding:US-ASCII> "\xFF".encoding → "\xFF".encoding → <Encoding:ASCII-<Encoding:ASCII-
8BIT>8BIT> "\u{3042}".encoding → "\u{3042}".encoding →
<Encoding:UTF-8><Encoding:UTF-8> "\u{3042 3044 3046}".encoding"\u{3042 3044 3046}".encoding
→ → <Encoding:UTF-8><Encoding:UTF-8>
String#[] → String#[] → 文字文字
1.8:1.8: String#[]String#[] → integer (1 byte)→ integer (1 byte) ““ あいうあいう””[0][0] → 0xE3 # UTF-8→ 0xE3 # UTF-8
1.9:1.9: String#[]String#[] → 1 → 1 文字 文字 stringstring ““ あいうあいう””[0][0] → "→ " ああ ""
String#lengthString#length
1.8:1.8: String#lengthString#length → byte length→ byte length ““ あいうあいう””.length.length → 9 (UTF-8)→ 9 (UTF-8) 1.9:1.9: String#lengthString#length → character length→ character length ““ あいうあいう””.length.length → 3→ 3 String#bytesizeString#bytesize → byte length→ byte length ““ あいうあいう””.bytesize.bytesize → 9 (UTF-8)→ 9 (UTF-8)
String is not EnumerableString is not Enumerable
String#each is removed.String#each is removed.
"hoge".each{|l|p l}"hoge".each{|l|p l}
→→ NoMethodErrorNoMethodError
String#each_*String#each_*String#each_byte String#each_byte (bytes)(bytes)
String#each_char String#each_char (chars)(chars)
String#each_line String#each_line (lines)(lines)
文字列の比較・結合文字列の比較・結合 同じエンコーディングの時に比較 結合可・同じエンコーディングの時に比較 結合可・
能能 バイト列として一致し、バイト列として一致し、
エンコーディングも等しい時に エンコーディングも等しい時に == == 成成立立(( 違うと 違うと ArgumentError)ArgumentError)
7bit7bit のみの文字列は特別のみの文字列は特別
正規表現マッチ正規表現マッチ
/(.)/ =~ "/(.)/ =~ " あいうあいう ""$1 → "$1 → " ああ ""
バイト列とのマッチバイト列とのマッチ
/\xE3\x81\x82/n =~ "/\xE3\x81\x82/n =~ " ああ ""→ → ArgumentError:ArgumentError: incompatible encoding regexp match incompatible encoding regexp match (ASCII-8BIT regexp with UTF-8 string)(ASCII-8BIT regexp with UTF-8 string)
両者とも 両者とも ASCII-8BIT ASCII-8BIT にして行うのが正解にして行うのが正解∵∵ これはバイト列処理であって、文字列処これはバイト列処理であって、文字列処理ではない理ではない
バイト列に対するマッバイト列に対するマッチチ
bytes = "Abytes = "Aああ ".force_encoding(".force_encoding(
"ASCII-8BIT")"ASCII-8BIT")/\xE3\x81\x82/ =~ bytes → /\xE3\x81\x82/ =~ bytes →
11/a/ =~ bytes → 0/a/ =~ bytes → 0
Script EncodingScript Encoding以下のエンコーディングを以下のエンコーディングを決定決定
文字リテラル文字リテラル文字列リテラル文字列リテラル正規表現リテラル正規表現リテラル
Magic Magic CommentComment
#!/bin/env ruby#!/bin/env ruby
# -*- coding: UTF-8 -*-# -*- coding: UTF-8 -*-
/coding[:=]\s*(?/coding[:=]\s*(?<encname><encname>
[\w.-]+)[^\w.-]/[\w.-]+)[^\w.-]/
-K option-K option
-K-K オプションは健在ですオプションは健在ですEncoding.external_encoding Encoding.external_encoding
に影響に影響script encoding script encoding にも影響すにも影響す
る。る。-E-E オプションは影響しない オプションは影響しない
(external encoding (external encoding にのみ影にのみ影響響 ))
script encodingscript encoding
通常のスクリプ通常のスクリプトト
1.1. magic magic commentcomment
2.2. -K-K
3.3. US-ASCIIUS-ASCII-E-E は影響しないは影響しない
-e-e およびおよび stdinstdin1.1. magic magic
commentcomment
2.2. -K or -K or ––EE
3.3. LocaleLocale
デフォルトはデフォルトはlocalelocale
String#inspect vs String#inspect vs String#dumpString#dump
String#inspectString#inspectぱっと見用ぱっと見用String#dump:String#dump:dumpdump 用用EscapeEscape 用には用には dumpdump を!を!
IO openIO open
open(path, "r:utf-8") open(path, "r:utf-8") {|f| puts {|f| puts f.gets }f.gets }
open(path, "r:utf-8:euc-jp") open(path, "r:utf-8:euc-jp") {.. }{.. }
open(path,open(path,"mode:external:inter"mode:external:internal")nal")
IO with encoding optionIO with encoding option
open(path, encoding: "utf-8")open(path, encoding: "utf-8") open(path,open(path, encoding: "utf- encoding: "utf-
8:euc-jp")8:euc-jp") open(path, encoding: open(path, encoding:
""external:internalexternal:internal")")
IO with encoding optionIO with encoding option
open(path,open(path,
external_encoding: "utf-8")external_encoding: "utf-8")open(path,open(path,
external_encoding: "utf-8external_encoding: "utf-8““,,
internal_encoding: "euc-jp")internal_encoding: "euc-jp")
Encoding.defult_externEncoding.defult_externalal
Default encoding for external Default encoding for external inputinput
-K or -E > locale-K or -E > locale
String as BytesString as BytesString#getbyte(index)String#getbyte(index)String#setbyte(index, String#setbyte(index, value)value)
String#bytesizeString#bytesize
transcodingtranscoding
MartinMartin さんよりさんより http://www.sw.it.aoyama.ac.jp/2008/
pub/RubyKaigiM17N.html
まとめまとめ Encoding Encoding を意識しようを意識しよう EncodingEncoding 独立なコードを書こう独立なコードを書こう String#encodeString#encode 便利便利
Magic comment Magic comment を書を書こうこう
今後の予定?今後の予定? Dir.open Dir.open 関連関連 その他 その他 encoding encoding を今扱えないメを今扱えないメ
ソッドソッド– Dir.glob, fnmatchDir.glob, fnmatch
String#encode (transcode) String#encode (transcode) のサのサポートポート
Unicode Unicode 版 版 Win32API Win32API の利用の利用 ??
RubyM17NRubyM17N の冒険の冒険はは
まだまだまだまだ始まったばかりだ 始まったばかりだ !!!!!!
質疑応答質疑応答
any questions?any questions?
戦略戦略
* UCS* UCS で行くかで行くか * * CSICSI で行くかで行くか
UCSUCS の場合の場合
* UCS* UCS を一つ決めるを一つ決める * * magic commentmagic comment を書くを書く * * UCSUCS 以外は変換以外は変換
CSICSI の場合の場合
* encoding* encoding に依存しないコードを書くに依存しないコードを書く * * magic commentmagic comment は不要は不要
FAQFAQ
Any questions?Any questions?
自動変換して結合自動変換して結合
* * 情報の欠落が発生するので困難情報の欠落が発生するので困難 * * US-ASCII US-ASCII で現在部分的に実現で現在部分的に実現
[0x3042,0x3044].pack("U")[0x3042,0x3044].pack("U")
* pack("U*") * pack("U*") の結果に の結果に encoding encoding をつをつけてほしいけてほしい
* * pack("U*") pack("U*") では では UTF-8 UTF-8 であると断定であると断定できるが、できるが、
* * pack("UC") pack("UC") では不定なので難しい。では不定なので難しい。
ライブラリのエンコーディングライブラリのエンコーディング
require require でエンコーディングの指定は不でエンコーディングの指定は不可能可能
US-ASCIIUS-ASCII で書こうで書こう