Gaucheでの文字列の作り方のメモ

いつも文字列を作る場面になってからマニュアルを探し始めるので、少しまとめておく。

データの文字列表現: x->string、write-to-string

データの文字列表現を得るときに使う。x->stringは文字列化にdisplayを使い、write-to-stringは文字列化にwriteを使う。
例:

gosh> (x->string "Scheme")
"Scheme"
gosh> (write-to-string "Scheme")
"\"Scheme\""
gosh> (define ls (list 1 #\a "bcd"))
ls
gosh> ls
(1 #\a "bcd")
gosh> (x->string ls)
"(1 a bcd)"
gosh> (write-to-string ls)
"(1 #\\a \"bcd\")"
gosh> (print (write-to-string ls))
(1 #\a "bcd")
#<undef>

ハッシュテーブルを文字列化したい場合は、util.listモジュールのhash-table->alist を使って連想リストに変換してから文字列にする(ただし、http://blog.practical-scheme.net/shiro/20100219-map-and-equivalenceを参照)。

format、文字列の補間

(format #f フォーマット文字列 …) は、指定されたフォーマットにもとづいて文字列を構築する(formatの詳細)。
フォーマット指示詞はチルダ「~」から始まり(パーセント「%」ではない)、a・s・d・b・o・x・*のどれかで終わる。これらの間にフォーマットの細かな指定を書く。指示の詳細は省略。

  • a … ascii出力。displayによる出力。
  • s … S式出力。writeによる出力。
  • d … 10進数。
  • b … 2進数。
  • o … 8進数。
  • x … 16進数。xを指定するかXを指定するかで違いがあって、xなら「abcdef」を使い、Xなら「ABCDEF」を使う。
  • * … 引数のカウンタをずらす。詳細略。

たいていは、数→d、文字列→a、リスト→aまたはsか。
例:

gosh> (let ((str "abc")
            (num 20))
        (format #f "~a = ~s   ~s = ~d = ~b = ~o = ~x"
          str str num num num num num))
"abc = \"abc\"   20 = 20 = 10100 = 24 = 14"

フォーマットの細かな指定が必要ないなら、文字列の補間を使うこともできる。

文字列を「"……"」と書く代わりに「#`"……"」と書いて、補間をしたい位置に「,式」または「,|変数名|」と書く。
(追記:0.9.4から文字列補完の構文が変更された。
「#`"……,式……"」「#`"……,|変数|……"」
ではなく
「#"……~式……"」「#"……~|変数|……"」になった)
例:

gosh> (let ((x "pqr")
            (y 123))
        #`"abc,|x|def,|y|ghi,(+ 456 78)jkl")
"abcpqrdef123ghi534jkl"

文字列の左や右に空白等を追加して長さをそろえたい場合、srfi-13のstring-pad、string-pad-rightというのもある。

gosh> (use srfi-13)
#<undef>
gosh> (string-pad "123" 10)
"       123"
gosh> (string-pad "123" 10 #\0)
"0000000123"

文字列の連結: string-append、string-concatenate[srfi-13]、string-join、tree->string[text.tree]、call-with-output-string、with-output-to-string

  • (string-append 文字列1 文字列2 …) …… 文字列1・文字列2…を繋げた文字列を作る。
  • (string-concatenate 文字列のリスト) …… (srfi-13モジュール) 文字列のリストを連結する。
  • (string-join 文字列のリスト [区切り文字列] [連結の仕方]) …… 文字列のリストを区切り文字列をはさんで連結する。区切り文字列を省略すると空白をはさむ。さらに必要なら連結の仕方をシンボルで指定する。infix、strict-infix、prefix、suffix。ファイルやディレクトリのパス名を作るには、file.utilモジュールのbuild-path もある。
gosh> (string-join '("abc" "def" "ghi") "--")
"abc--def--ghi"
  • (tree->string 文字列のツリー) …… (text.treeモジュール) 文字列のツリーを連結する。
  • call-with-output-string、with-output-to-string: 文字列ポートを使って文字列を作る。条件によって連結する文字列を変えたい場合に便利。
(with-output-to-string 
  (lambda ()
    (when x? (display "abc"))
    (if y?
      (display ".")
      (display "/"))
    (display "def")))

とか。

文字列の置換: string-replace[srfi-13]、regexp-replace、regexp-replace-all、string-tr[text.tr]、path-swap-extension [file.util]、uri-encode-string[rfc.uri]

  • (string-replace str1 str2 start end) …… (srfi-13モジュール) 文字列str1のstart文字目からend文字目の前までをstr2で置き換える。
gosh> (use srfi-13)
#<undef>
gosh> (string-replace "0123456789" "wxyzabcd" 2 5)
"01wxyzabcd56789"
  • (regexp-replace 正規表現 str1 str2) …… 文字列str1中で正規表現に最初にマッチした部分を文字列str2で置き換える。マッチした文字列・部分文字列は「\0」「\1」…、バックスラッシュは「\\」で表す(文字列リテラル中では「\\0」「\\1」…、「\\\\」と書く)。置き換え文字列str2の代わりに、マッチオブジェクトを受け取り置き換え文字列を返す手続きを渡すこともできる。
  • (regexp-replace-all 正規表現 str1 str2) …… 文字列str1中で正規表現にマッチした部分それぞれを全てstr2に置き換える。あとはregexp-replaceと同じ。
gosh> (regexp-replace #/beta/ "alpha beta gamma delta" "B")
"alpha B gamma delta"
gosh> (regexp-replace #/beta/ "alpha beta gamma delta" "B\\\\")
"alpha B\\ gamma delta"
gosh> (regexp-replace #/beta/ "alpha beta gamma delta" "B\\0C")
"alpha BbetaC gamma delta"
gosh> (regexp-replace #/(beta).*(gamma)/ "alpha beta gamma delta" "|\\2|\\1|\\0|")
"alpha |gamma|beta|beta gamma| delta"
gosh> (use srfi-13)
#<undef>
gosh> (regexp-replace #/beta.*gamma/i "AlPhA bEtA GaMmA dElTa"
        (lambda (m) (string-titlecase (m 0))))
"AlPhA Beta Gamma dElTa"
  • (string-tr str from to) …… (text.trモジュール) 文字列strに対して、fromにリストされた各文字を対応するtoの文字に置換する。ただし指示文字列中で「-」「*」「\」は、特別な意味を持つ。
gosh> (use text.tr)
#<undef>
gosh> (string-tr "abcdefghi" "bdfh" "<<%&")
"a<c<e%g&i"
gosh> (string-tr "abcdefghi" "bdfh" "X*3Y")
"aXcXeXgYi"
gosh> (string-tr "abcdefghi" "bdfh" "Y*")
"aYcYeYgYi"
gosh> (string-tr "abcdefghi" "bdfh" "A-D")
"aAcBeCgDi"
gosh> (string-tr "aBcDeFgHi" "c-fi" "%\\-*")
"aB%D-FgH-"
gosh> (string-tr "abcdef abcxyz zyxwdcba" "a-z" "A-N" :delete #t)
"ABCDEF ABC DCBA"
  • (path-swap-extension path ext) …… (file.utilモジュール) pathの拡張子をextに置き換える。extが#fの場合は拡張子を取り除く。
gosh> (use file.util)
#<undef>
gosh> (path-swap-extension "aaa/bbb/ccc.txt" "scm")
"aaa/bbb/ccc.scm"
gosh> (path-swap-extension "aaa/bbb/ddd" "scm")
"aaa/bbb/ddd.scm"
gosh> (path-swap-extension "aaa/bbb/eee.txt" #f)
"aaa/bbb/eee"
  • (uri-encode-string str) …… (rfc.uriモジュール) %を使ったエスケープによって文字を置換する。てきとうなファイル名を作るときなどにも利用できる。
gosh> (use rfc.uri)
#<undef>
gosh> (uri-encode-string "/aaa/bbb/ccc&ddd")
"%2faaa%2fbbb%2fccc%26ddd"

ファイルの全内容を文字列にする: file->string[file.util]、port->string

  • (file->string file) …… (file.utilモジュール) 文字列fileで指定したファイルの内容を文字列で返す。
  • (port->string port) …… 入力ポートからEOFまで読み込んで文字列にして返す。

パス名、ファイル名の分解・構築

パス名を作る: build-path[file.util]、path-swap-extension[file.util]
  • (build-path base-path component …) …… (file.utilモジュール) パスbase-pathにcomponent … を追加した文字列を作る。
gosh> (use file.util)
#<undef>
gosh> (build-path "/aa/bb" "cc" "dd.scm")
"/aa/bb/cc/dd.scm"
  • (path-swap-extension path ext) …… (file.utilモジュール) pathの拡張子をextに置き換える。extが#fの場合は拡張子を取り除く。
パス名の分解: decompose-path[file.util]、string-split
  • (decompose-path path) …… (file.utilモジュール) パスpathをディレクトリ、ファイル名(拡張子を除く)、拡張子に分解する。
gosh> (use file.util)
#<undef>
gosh> (receive (dir file ext) (decompose-path "aaa/bbb/ccc.ddd")
        (list dir file ext))
("aaa/bbb" "ccc" "ddd")
gosh> (receive (dir file ext) (decompose-path "aaa/bbb/")
        (list dir file ext))
("aaa/bbb" #f #f)
gosh> (receive (dir file ext) (decompose-path "aaa/bbb/eee")
        (list dir file ext))
("aaa/bbb" "eee" #f)

ディレクトリ部分がない場合、dirはカレントディレクトリになる。

gosh> (receive (dir file ext) (decompose-path "fff.ggg")
        (list dir file ext))
("." "fff" "ggg")

これは?

gosh> (receive (dir file ext) (decompose-path "/")
        (list dir file ext))
("" #f #f)
  • (string-split path "/") …… pathをスラッシュ「/」で分解する。
gosh> (string-split "/aaa/bbb/ccc.ddd" "/")
("" "aaa" "bbb" "ccc.ddd")

URI

  • uri-compose …… (rfc.uriモジュール) URIを表す文字列を作る。引数はキーワード引数で指定する。
 gosh> (use rfc.uri)
#<undef>
gosh> (uri-compose :scheme "http" :host "aaa.bbb" :path "/index.html" :query "x=3&y=4")
"http://aaa.bbb/index.html?x=3&y=4"
gosh> (uri-compose :scheme "http" :host "aaa.bbb" :path* "/index.html?x=3&y=4")
"http://aaa.bbb/index.html?x=3&y=4"

URIの分解にはuri-parseその他を使う。

日付を表す文字列を作る

  • (date->string x [format]) …… (srfi-19モジュール) <date>型オブジェクトxを文字列にする。format文字列を使えば日付文字列の形を細かく指定できる。
    • 「~Y」「~y」…… 西暦、西暦下2桁(00..99)
    • 「~B」「~b」「~m」…… 月完全形(January...December)、月省略形(Jan...Dec)、月数字(01..12)
    • 「~d」「~e」…… 日にち0補充(01..31)、日にち空白補充( 1..31)
    • 「~A」「~a」「~w」…… 曜日完全形(Sunday..Saturday)、曜日省略形(Sun..Sat)、曜日数字(0..6)
    • 「~H」「~k」…… 時間24時間制0補充(00..23)、時間24時間制空白補充( 0..23)
    • 「~I」「~l」…… 時間12時間制0補充(01..12)、時間12時間制空白補充( 1..12)
    • 「~p」…… AM、PM
    • 「~M」…… 分0補充(00..59)
    • 「~S」…… 秒0補充(00..60)。60は閏秒
    • その他いろいろ
gosh> (use srfi-19)
#<undef>
gosh> (date->string (current-date) "~Y ~m ~d ~l ~p ~M ~S")
"2010 05 03  8 PM 57 17"

HTML

  • text.html-liteモジュールのいろいろな手続き + text.treeモジュール。
  • (srl:sxml->html sxmlオブジェクト)、(srl:sxml->html-noindent sxmlオブジェクト) …… (sxml.serializerモジュール) sxmlオブジェクトからhtml形式の文字列を得る。

CSV

  • ((make-csv-writer separator) oport string-list) …… (text.csvモジュール) 文字列のリストstring-listをCSV形式で出力ポートoportに出力する。出力時、フィールドの区切りにseparatorを使う。
gosh> (call-with-output-string
        (lambda (oport)
          (let ((w (make-csv-writer ", ")))
            (w oport '("a" "1" "bbb"))
            (w oport '("c" "2" "dd\"ee"))
            (w oport '("f" "3" "ggg ")))))
"a, 1, bbb\nc, 2, \"dd\"\"ee\"\nf, 3, \"ggg \"\n"
gosh> (let ((w (make-csv-writer ", ")))
        (w (current-output-port) '("a" "1" "bbb"))
        (w (current-output-port) '("c" "2" "dd\"ee"))
        (w (current-output-port) '("f" "3" "ggg ")))
a, 1, bbb
c, 2, "dd""ee"
f, 3, "ggg "
#<undef>