文字列, テキスト型

Haskell の文字列は, 歴史的な事情で, 若干複雑になっています。

Char型

1文字を表すのは Char型です。値の意味は Unicode code point です。なので、厳密には「文字」ではない。

リテラルは「'」で文字を囲みます。

Prelude> :type 'あ'
'あ' :: Char

文字リテラルでは, \x に続けて16進数で10ffffまでのcode point を指定できます。これを超える値の場合は out of range エラー。

Char型は次の型クラスのインスタンスです。

Bounded Enum Eq Data Ord Read Show Ix Storable IsChar PrintfArg

TODO: このほか data family URec のインスタンス?

String型

組込みの文字列型は String です。String[Char], つまりCharのリスト型の別名になっています。

String は素朴な1文字単位のリストなので, 想像どおり, パフォーマンスに難があります。過去との互換性のために残っています。

新しいプログラムでは, バイト列がほしいときは ByteString, 文字列がほしいときは Text データ型を使ってください。

type String = [Char] 	-- Defined in `GHC.Base'

リテラルは「"」で囲みます。String型は単なるリストなので、その値の操作については, リストに適用できる関数がそのまま使えます。

次のプログラムは, ファイルの先頭を表示します。

Haskell
[POPUP]
  1. doHead :: Int -> String -> String
  2. doHead n cs =
  3. unlines $ take n $ lines cs
  4. main = do
  5. cs <- readFile "str1.hs"
  6. putStr $ doHead 5 cs

それぞれの関数:

readFile :: FilePath -> IO String
ファイルを読み込む「アクション」を返します。ここで実際にファイルを読み込むのではなく, 必要があったときに実際に読み込まれます。
lines :: String -> [String]
文字列を行の集まり(リスト)に分割します。結果の各文字列には, 改行文字 '\n' は含まれません。

ただし, 元の文字列の末尾の改行文字は単に除去されるため, 元の文字列に厳密に復元できるようにはなっていません。

Prelude> lines "fuga\r\n"
["fuga\r"]
take :: Int -> [a] -> [a]
整数nとリストを引数に取り, 先頭のn要素を取り出します。
unlines :: [String] -> String
行の集まりを, 改行文字 '\n' をはさんで文字列にします。

doブロックについては, またページを替えて説明しますが, とりあえずここでは,

ByteStringデータ型

bytestring パッケージの Data.ByteString モジュール, ByteString データ型は, 単なるバイトの配列です。型名に〜Stringが含まれていますが, 文字列は関係ありません。

ByteString は, 次の型クラスのインスタンスです。

Eq Data Ord Read Show IsString Monoid NFData

とはいえ, 単に右から左へデータを移すようなときは, ByteString が使えます。

Haskell
[POPUP]
  1. {-# LANGUAGE OverloadedStrings #-}
  2. import Data.ByteString as BS
  3. import Data.ByteString.Char8 as BSC
  4. import System.IO
  5. main :: IO ()
  6. main = do
  7. Prelude.putStr "何か入力> "
  8. hFlush stdout
  9. str <- BS.getLine
  10. BSC.putStrLn $ "str = " `BS.append` str
  11. Prelude.putStrLn $ (show $ BS.length str) ++ " bytes"

OverloadedStrings で、文字列リテラルが String型以外になりえます。IsString型クラスのインスタンス (で型が合うもの) になる。

実行例

何か入力> ああ愛
str = ああ愛
9 bytes

単にそのまま表示するだけなら, ただのバイト列なので, 化けません。この例だと, 文字数は UTF-8によるバイト数の9になっています。

Text

本物の文字列は, Text 型です。

入出力は ByteString を使い, 文字列に変換して使います。

Haskell
[POPUP]
  1. import Data.Text
  2. import Data.Text.Encoding
  3. import qualified Data.ByteString
  4. countChars :: Data.ByteString.ByteString -> Int
  5. countChars s = Data.Text.length $ Data.Text.Encoding.decodeUtf8 s
  6. main = do
  7. cs <- Data.ByteString.readFile "str2.txt"
  8. print $ countChars cs

"str2.txt"ファイル (UTF-8で保存。)

あああAbc23はひふ

実行結果:

$ runhaskell str2.hs 
12

改行文字込みで, 正しく12文字とカウントできている。

Text型については、別ページに続きます; テキスト型の周辺