SCSHのもう1つの組み込みDSLはawkだ。awkとはもちろんテキストストリーム内のレコードやフィールドをパースするのに役立つUNIXツールのことで、SCSHのDSLのawkはマクロ「awk」を使用して呼び出し、レコードとフィールドの切り分け方やレコードの取捨選択のための基準を指定することができる。awk構文ではファイル内のレコードやフィールドをループで処理する作業が隠ぺいされていて、レコードがどのようなときにレコードをどのようにするのかを定義できる。
awk構文には、レコード処理関数、処理関数が返す値の名前、条件文のリストを与える必要がある。レコード処理関数は、入力ストリームからレコードを読み取って、そのレコードからパースしたレコードとフィールドを返す。フィールドリーダーは、SCSHの関数であるfield-readerを使用して作成できる。レコード処理関数は通常、読み取ったレコードと、フィールドのリストを返す。そのような各値は、構文に与えておいた変数名のおかげで簡単に参照できる。
グラフや円グラフやスプレッドシート用のデータを保存するためによく使われる形式の1つに、CSV(Comma Separated Value)がある。CSVでは一行ごとにレコードが記述されていて、各フィールドは「,」で区切られている。
a,3,apple
b,23,banana
c,1,camel
上に示した例では、1行ごとのレコードが3つあって、各レコードには3つのフィールドがある。awk構文はこれらをすべて文字列に変換して、処理を行うためのリストの中に保存する。
今後実際に対処する必要が出てくる可能性もあるより現実的なCSVファイルの例として、連絡先を保存したファイルを扱ってみよう。CSV形式のファイルに保存しておくとバックアップの観点から都合が良い場合があったり、別プログラムにインポートする作業を自動化するのに便利だったりすることがある。
Name,E-mail Address,Notes,E-mail 2 Address,E-mail 3 Address,Mobile Phone,\
Pager,Company,Job Title,Home Phone,Home Phone 2,Home Fax,Home Address,\
Business Phone,Business Phone 2,Business Fax,Business Address,Other Phone,\
Other Fax,Other Address
Hiro Protagonist,,"Last of the freelance hackers",,,,,,,,,,,,,,,,,
Mr. Lee,lee@greaterhongkong.com,,,,,,Mr. Lee's Greater Hong Kong,President,,,,,,,,,,,
Casimir Radon,cradon@megaversity.edu,"Physics club head, friend of Sarah\",,,,,,,555.555.1234,,,,,,,,,
このCSVファイルでは、1行目が各フィールド名のリストになっている。そのため一行目は読み飛ばす必要があるのだが、SCSHのawk構文ではこのことを簡単に行うことができる。
以下のコードは、メールアドレスが含まれているレコードについてのみ、名前とメールアドレスをHTML形式で出力する。
#!/usr/bin/scsh -s
!#
(define read-csv (field-reader (infix-splitter "," 20)))
(define (empty-field? x) (string= x ""))
(define (start-html page-title) (format #t #<<END
<html>
<head>
<title>~a</title>
</head>
<body>
<h1>~a</h1>
<p>
END
page-title page-title))
(define (end-html) (display " </p>
</body>
</html>
"))
(define (display-email-address email name)
(format #t #<@ <a href="mailto:~a">~a</a><br/>~%@ email name name))
(with-input-from-file "contacts.csv"
(lambda ()
(define $ list-ref)
(start-html "Contact List")
(awk (read-csv) (record fields) n-records ()
(range: 1 #f (if (not (empty-field? ($ fields 1)))
(display-email-address ($ fields 1) ($ fields 0)))))
(end-html)))
このCSVファイルでは、フィールドの中にコンマ「,」が含まれているものもある。このことはパースの際に大きな問題となり得るが、SCSHでは簡単に対処できる。ダブルクォートで囲まれたフィールドにはコンマが含まれていても良いことになってるので、CSVのレコードとフィールドを読み取るための関数を定義する際には特に気にせずにコンマをフィールドのデリミタとして指定するのにinfix-splitterを使用すればよい。
次の行は、与えられた文字列が空かどうかを確認する関数empty-field?を定義している。その次のstart-htmlはHTMLページの先頭を出力するための関数で、ページのタイトルを指定できる。この関数ではHTMLの内容にヒア文字列を使用しているので、エスケープしなくてもダブルクォートを使用できる。end-html関数は、単にHTMLページの最後の部分を出力している。display-email-address関数は、ヒア文字列を使用して、メールアドレスへのHTMLリンクを作成して出力している。ここではヒア文字列のデミリタとして「@」記号を使用している。
そして最後にcontacts.csvファイルを開いてlambda関数を実行している。なおファイルはlambda関数の最後で自動的に閉じられる。lambda関数の中では、まずstart-html関数が呼ばれ、次にread-csvレコードリーダーを用いたawk構文が使用されている。そしてレコードを読み取るたびに、条件に該当するかどうかの確認をする。この例では、レコード番号が1 より大きければ次の文を実行して、現在のレコードの2つめのフィールド(メールアドレス)が空かどうかを確認している。空でなければメールアドレスを出力する。
なおawkでは、行番号や、読み取ったレコードが正規表現にマッチするかどうかや、単純なif条件など、ほかのタイプの条件も指定できる。
Copyright © 2010 OSDN Corporation, All Rights Reserved.