読者です 読者をやめる 読者になる 読者になる

Crystal の文字列は UTF-8 固定でつらい

これは「Ruby脳にはCrystalつらい Advent Calendar 2015」の19日目の記事です。

qiita.com

昨日の記事で Crystal の文字列は UTF-8 だと書きましたが、実行時に外部から UTF-8 以外の文字列を与えるとどうなるのでしょうか。

p File.open("/dev/urandom").gets
% crystal hoge.cr             
Invalid byte sequence in UTF-8 string (InvalidByteSequenceError)
[4350519] *CallStack::unwind:Array(Pointer(Void)) +87
[4350410] *CallStack#initialize<CallStack>:Array(Pointer(Void)) +10
[4350362] *CallStack::new:CallStack +42
[4438767] *Exception +31
[4438701] *InvalidByteSequenceError#initialize<InvalidByteSequenceError, String>:CallStack +29
[4438641] *InvalidByteSequenceError::new<String>:InvalidByteSequenceError +97
[4438533] *InvalidByteSequenceError::new:InvalidByteSequenceError +21
[4455450] *Char::Reader#invalid_byte_sequence<Char::Reader>:NoReturn +10
[4454605] *Char::Reader#decode_current_char<Char::Reader>:Char +157
[4454434] *Char::Reader#initialize<Char::Reader, String>:Char +34
[4454338] *Char::Reader::new<String>:Char::Reader +98
[4348954] *String#inspect<String, IO::FileDescriptor>:IO::FileDescriptor +90
[4342179] *p<String?>:String? +115
[4333778] ???
[4340752] main +32
[139929057270336] __libc_start_main +240
[4331145] _start +41
[0] ???

実行時エラーになりました。

ただし、このエラーは読み込んだ文字列を文字単位で評価しようとした時に発生します。

line = File.open("/dev/urandom").gets
if line.is_a? String  # gets は nil を返すことがあるためこの if が必要
  p line.size
  line.each_char do |c|
    p c
  end
end
% crystal hoge.cr
96
'܄'
'\u{5}'
Invalid byte sequence in UTF-8 string (InvalidByteSequenceError)
[4349767] *CallStack::unwind:Array(Pointer(Void)) +87
[4349658] *CallStack#initialize<CallStack>:Array(Pointer(Void)) +10
[4349610] *CallStack::new:CallStack +42
[4438095] *Exception +31
[4438029] *InvalidByteSequenceError#initialize<InvalidByteSequenceError, String>:CallStack +29
[4437969] *InvalidByteSequenceError::new<String>:InvalidByteSequenceError +97
[4437861] *InvalidByteSequenceError::new:InvalidByteSequenceError +21
[4454746] *Char::Reader#invalid_byte_sequence<Char::Reader>:NoReturn +10
[4453901] *Char::Reader#decode_current_char<Char::Reader>:Char +157
[4334218] ???
[4341200] main +32
[140711960898112] __libc_start_main +240
[4331017] _start +41
[0] ???

File#gets でもなく、String#size でもなく、String#each_char で UTF-8 として不正な文字を取り出した時にエラーになっています。

Ruby と異なり、エンコーディングを変換したり UTF-8 として正しいバイト列かどうかを判定する方法もありません。つらい。