Rev.01 1997/11/24 風つかい ■ Icon > Icon散歩道 はじめに 風つかい Iconは アリゾナ大学の Ralph E Griswold教授のグループによって開発されてい るテキスト処理言語です。 Iconは テキスト解析に 威力を発揮する 豊富なデータ構造と 強力な制御構造を を備えています。 英語の国で開発されている言語ですが、日本語の処理も不自由 しませんし、もっともっと使われてよい言語と思います。 しかし、未だ 日本語の入門書が ありませんので、 AWKについて、ある程度の 知識を お持ちになっている方を 対象として、入門講座を書きました。 ・テキスト解析言語Icon入門講座 ---AWKerのための Icon入門--- ・Icon日記 ---テキスト解析言語Icon入門講座2--- ・Icon雑記帳 ---テキスト解析言語Icon入門講座3--- この Icon散歩道は その続編として、その後 作ったプログラムや 前の 講座で 書き落とした ものを、まとめたものです。 前の 3編の講座と いっしょに ご覧になって Iconを使って頂けると うれしい です。 Iconのプログラムおよびライブラリーは、次の所から 入手できます。 http://www.cs.arizona.edu/icon/index.html この講座は、主に PCVANのPIGのテキストデータ処理会議室にアップロードした ものに加筆・修正を行ったものです。 Iconは PDSですので、この入門講座も同じ扱いとします。(転載・編集自由) (This textbook is in the public domain.) 目次 はじめに (1) 再び構造体 tree表示(1) (2) suspend...do... (3) 再び5クイーン (4) 再び構造体 tree表示(2) (5) 再び構造体 tree表示(3) (6) 再び wild.icn(1) (7) 再び wild.icn(2) (8) 再び dir2tree (9) 再び x2tree(1) (10) 再び x2tree(2) (11) Icon迷い道 (1)system() (12) Icon迷い道 (2)download数 集計 (13) Icon迷い道 (3)CSV:定義ファイルの読込 (14) Icon迷い道 (4)CSV:定義ファイルの解析 (15) Icon迷い道 (5)CSV:拡張子変更(1) (16) Icon迷い道 (6)CSV:拡張子変更(2) (17) Icon迷い道 (7)CSV:CSV2TB (18) Icon迷い道 (8)CSV:TB2CSV (19) ちょっと Icon(1)data整形(1) (20) ちょっと Icon(2)data整形(2) (21) ちょっと Icon(3)data整形(3) (22) ちょっと Icon(4)data整形(4) (23) Icon回り道 (1)重複しないランダムな10個の数(1) (24) Icon回り道 (2)重複しないランダムな10個の数(2) (25) Icon回り道 (3)重複しないランダムな10個の数(3) (26) Icon回り道 (4)Usageが... (27) Icon回り道 (5)a < b が、値を持つ (28) ちょっと Icon(5)Wicon beta8 (29) ちょっと Icon(6)Icon Newsletter No.53 (30) ちょっと Icon(7)Icon Ver.9.3(Windows95/NT) むすび 風つかい (TRA11936@biglobe.ne.jp/PFF01531@niftyserve.or.jp) < IconのWWWは、http://www.cs.arizona.edu/icon/index.html> BGM: 愛の言霊/サザン・オールスターズ (iconlec4.txt 1997/11/24) ■ Icon > Icon散歩道(1) 再び 構造体tree表示(1)風つかい Icon雑記帳 に引き続き、Icon迷い道 じゃなかった Icon散歩道 を始めます。 今回こそは、テキスト解析らしい programを作ってみたいと思います。 テキスト解析といっても、 ・簡単な数式解析 と ・簡単なアセンブラ あたり を目標にしています。 と、思っていたのですが、Icon雑記帳を読み返していたら、う〜む。ミスが 見つかりました。 その修正から始めます。 Icon入門講座をやっていまして、アップロードする原稿や programは、その前 に、チェックしている つもりなのですが、大抵 1つや 2つのミスが 含まれて います。 しかし、気がつくのは、大抵、アップロードした後に、自分のメッセージを 読みだしコマンドで、読んだ時です。 もう手遅れのタイミングです。 アップロードするまでは、若干緊張しています。 それが終わった後に、少し ホッとした気分で、自分のメッセージを読み返す訳なのですが、 そういう時に 自分のミスが見つかります。別の自分にならないと、見つからないみたいです。 書いた後に、1日位 寝かせておいて、その後に もう1回チェックすると良い のですが、こらえ性が無くて。 修行が足りないな〜。 と、言うわけで、Icon雑記帳(19)の x2tree03.icnの修正版です。 内容は、コメントに書き込んでありますので、ご覧下さい。 -----^ X2TREE05.ICN ( date:97-05-06 time:01:17 ) -----------" #↑listの serial No suspend " " || x2tree(x,"") #↑初期化フラグ end procedure x2tree(x,init) # generator # show the tree structure of x static T # serial No 登録用 set登録用 table static宣言を して # おかないと、再帰したときに、未定義 errorとなる。 if \init then { # Rev.1.2 T := table() T["list"] := set() # ここは、table(set())とすると、全て同じ setが T["set"] := set() # 割り当てられてしまうので、個別に初期化する。 T["table"] := set() if (t := type(x)) == ("list" | "set" | "table") then insert(T[t],serial(x)) # serial Noを T[type(x)]に登録 } n := *x # 枝の末端検出用カウンタ every xx := !x do { # 構造体の要素を1つ づつ取り出す n -:= 1 case t := type(xx) of { # 構造体のタイプにより、 "string" : suspend "+ " || left(t,7) || " " || jimage(xx) #↓list,set,tableなら、typeを書出して、その中身をまた解析する。 "list" | "set" | "table" : { #↓再帰構造で なければ。(T[type(xx)] に既に登録済みなら再帰構造) if not member(T[t],m := serial(xx)) then { insert(T[t],m) # serial Noを T[type(xx)]へ登録 suspend "+ <" || t || " " || string(m) || ">" suspend (if n > 0 then "| " else " ") || x2tree(xx) #↑枝の途中 #↑枝の末端 # 再帰↑ delete(T[t],m) #←serial No 登録を削除 } else { # 再帰構造 ならば、書き出すとキリが無いので打止め return "+ <" || t || " " || string(m) || "> (Recursion)" } } #↓その他なら、typeと値を書き出す。 default : suspend "+ " || left(t,7) || " " || image(xx) } } end # test driver procedure main() #listの test L1 := ["A","B"] L2 := ["C","D"] L3 := ["E","F"] put(L1,L2) put(L2,L3) put(L1,L3) # 別枝 test put(L3,L1) # listの再帰 every write(x2t(L1) \20) # \20は、テストをドジッた時に、20個で止めるため。 write() #tableの test T := table() T["A"] := "G" T["C"] := L2[1] T["E"] := L3 T[L1] := L1[1] put(L3,T) # tableの再帰 every write(x2t(T) \20) write() #setの test L4 := ["H","I"] S1 := set(["L","M"]) S := set(["J","K",L4]) insert(S,S1) insert(S1,S) # setの再帰 every write(x2t(S) \20) end -----$ X2TREE05.ICN ( lines:98 words:380 ) ----------------- + string "A" + string "B" + | + string "C" | + string "D" | + | + string "E" | + string "F" | + (Recursion) + + string "E" + string "F" + (Recursion) + string "C" + | + string "E" | + string "F" | + | | + string "A" | | + string "B" | | + | | | + string "C" | | | + string "D" | | | + (Recursion) | | + (Recursion) | +
(Recursion) + string "A" + string "G" + string "K" + | + string "M" | + string "L" | + (Recursion) + | + string "H" | + string "I" + string "J" -----$ X2TREE05 ( lines:42 words:155 ) --------------------- BGM: Goodbye Day /来生たかお (icon_301.txt 1997/05/05 PCVAN PIG) ■ Icon > Icon散歩道(2) suspend...do... 風つかい 前回、Icon雑記帳(19)で取り上げた x2tree03.icnのミスを 修正しました が、私は、どうも suspend...do...の構文が、理解できていない と思います。 そこで、動作確認の実験 programを作ってみました。 こういう構成の programです。 main() test_a() test_b() ------------------ ------------------ ------------------ | main() | | | | | | every test_a() <--- suspend...test_b() <--- suspend... | | | | do... | | do... | ----------------- ------------------ ------------------ 実験 programです。 -----^ DO_01.ICN ( date:97-05-04 time:23:28 ) -------------- BGM: いとおしいグレイ/篠原美也子 (icon_302.txt 1997/05/05 PCVAN PIG) ■ Icon > Icon散歩道(3) 再び 5クイーン 風つかい 前回 suspend...do...の構文 の動作確認を 行いましたが、他の programでも 誤解して 使っているところが 無いかと、探しました。 Icon日記の 5-Queensの programでも、Queenの置き直しのところで、使ってい ます。 ここは、特に do...を使う必要は、ありませんね。 -----^ 5QUEEN1D.ICN ( date:97-05-04 time:20:02 ) ----------- BGM: いとおしいグレイ/篠原美也子 (icon_303.txt 1997/05/05 PCVAN PIG) ■ Icon > Icon散歩道(4) 再び 構造体tree表示(2)風つかい Icon散歩道(1)で、x2tree03.icnの ミス修正版を 説明しましたが、どうし て ミスに気がついたか というと、実は、Version Upをしようと、思いまして、 テスト・ケースを増やしてみて、気がつきました。 今回は、その Version Upです。 Iconの tableは、AWKでいう 連想配列です。 tableは、key と valueを持っています。 前の programでは、valueしか表示し ません。 x2treeは、programの動作確認 のための toolとして作りましたが、 tableでは、value表示だけでなく、key表示も欲しい! ということで、key表示を追加します。 尚、Iconでは、keyにも 構造体(list,set,table)が、使えます。 しかし、keyに 構造体を使っても、そちらは tree表示はしません。 (そんな 複雑 tree表示は、思いつかないよ〜。) -----^ X2TREE06.ICN ( date:97-05-05 time:03:26 ) -----------" #↑listの serial No suspend " " || x2tree(x,"") #↑初期化フラグ end procedure x2tree(x,init) # generator # show the tree structure of x static T # serial No 登録用 set登録用 table static宣言を して # おかないと、再帰したときに、未定義 errorとなる。 if \init then { # 初期化を initialでやると、同じ program内で別の # 構造体 tree表示のために、再び x2tを 呼んだ時に # 実行されなくて状態が残るので、フラグで初期化指示 T := table() T["list"] := set() # ここは、table(set())とすると、全て同じ setが T["set"] := set() # 割り当てられてしまうので、個別に初期化する。 T["table"] := set() if (t := type(x)) == ("list" | "set" | "table") then insert(T[t],serial(x)) # serial Noを T[type(x)]に登録 } n := *x # 枝の末端検出用カウンタ if (tt := type(x)) ~== "table" then { # not table every xx := !x do { # 構造体の要素を1つ づつ取り出す n -:= 1 t := type(xx) val_disp := if t == ("list" | "set" | "table") then "+ <" || t || " " || string(m := serial(xx)) || ">" else "+ " || left(t,7) || jimage(xx) case t of { # 構造体のタイプにより、 "string" : suspend val_disp #↓list,set,tableなら、typeを書出して、その中身をまた解析する。 "list" | "set" | "table" : { #↓再帰構造で なければ。(T[type(xx)] に既に登録済みなら再帰構造) if not member(T[t],m) then { insert(T[t],m) # serial Noを T[type(xx)]へ登録 suspend val_disp suspend (if n > 0 then "| " else " ") || x2tree(xx) #↑枝の途中 #↑枝の末端 # 再帰↑ delete(T[t],m) #←serial No 登録を削除 } else { # 再帰構造 ならば、書き出すとキリが無いので打止め return val_disp || " (Recursion)" } } #↓その他なら、typeと値を書き出す。 default : suspend val_disp } } } else { # table every xkey := key(x) do { # tableの keyを1つ づつ取り出す n -:= 1 tk := type(xkey) xx := x[xkey] t := type(xx) val_disp := if t == ("list" | "set" | "table") then "+ <" || t || " " || string(m := serial(xx)) || ">" else "+ " || left(t,7) || jimage(xx) key_disp := if tk == ("list" | "set" | "table") then " <- <" || tk || " " || string(serial(xkey)) || ">" else " <- " || left(tk,7) || jimage(xkey) case t of { # 構造体のタイプにより、 "string" : suspend val_disp || key_disp #↓list,set,tableなら、typeを書出して、その中身をまた解析する。 "list" | "set" | "table" : { #↓再帰構造で なければ。(T[type(xx)] に既に登録済みなら再帰構造) if not member(T[t],m) then { insert(T[t],m) # serial Noを T[type(xx)]へ登録 suspend val_disp || key_disp suspend (if n > 0 then "| " else " ") || x2tree(xx) #↑枝の途中 #↑枝の末端 # 再帰↑ delete(T[t],m) #←serial No 登録を削除 } else { # 再帰構造 ならば、書き出すとキリが無いので打止め return val_disp || key_disp || " (Recursion)" } } #↓その他なら、typeと値を書き出す。 default : suspend val_disp || key_disp } } } end # test driver procedure main() #listの test L1 := ["A","B"] L2 := ["C","D"] L3 := ["E","F"] put(L1,L2) put(L2,L3) put(L1,L3) # 別枝 test put(L3,L1) # listの再帰 every write(x2t(L1) \20) # \20は、テストをドジッた時に、20個で止めるため。 write() #tableの test T := table() T["A"] := "G" T["C"] := L2[1] T["E"] := L3 T[L1] := L1[1] put(L3,T) # tableの再帰 every write(x2t(T) \20) write() #setの test L4 := ["H","I"] S1 := set(["L","M"]) S := set(["J","K",L4]) insert(S,S1) insert(S1,S) # setの再帰 every write(x2t(S) \20) end -----$ X2TREE06.ICN ( lines:133 words:507 ) ---------------- BGM: いとおしいグレイ/篠原美也子 (icon_304.txt 1997/05/05 PCVAN PIG) ■ Icon > Icon散歩道(5) 再び 構造体tree表示(3)風つかい x2treeに、tableの key表示を追加した programの動作結果です。 tableの部分が、若干、混みあった表示になりますが、test toolですので、 まあ、いんじゃない。 と、納得しましょう。 -----^ X2TREE06 ( date:97-05-05 time:03:26 ) --------------- + string "A" + string "B" + | + string "C" | + string "D" | + | + string "E" | + string "F" | + (Recursion) + + string "E" + string "F" + (Recursion)
+ string "C" <- string "C" + <- string "E" | + string "E" | + string "F" | + | | + string "A" | | + string "B" | | + | | | + string "C" | | | + string "D" | | | + (Recursion) | | + (Recursion) | +
(Recursion) + string "A" <- + string "G" <- string "A" + string "K" + | + string "H" | + string "I" + | + string "M" | + string "L" | + (Recursion) + string "J" -----$ X2TREE06 ( lines:42 words:167 ) --------------------- BGM: そのままの君でいて/岡本真夜 (icon_305.txt 1997/05/05 PCVAN PIG) ■ Icon > Icon散歩道(6) 再び wild.icn(1) 風つかい 近ごろ、"再び" というタイトルが多いですね〜。う〜む。以前の講座のミス が、みつかるのが多いな〜。 ということで、今回は Icon入門講座(16)で 作った wild.icnの修正版で す。 Iconの Program 例えば、abcd.icnを compileして、動かした結果のfile名を 以前は、abcd.txtとしていたんですが、txtの拡張子をつけるのも面倒というこ とで、近ごろは、ただ abcdとしています。 これを、YAB形式にしようと、IYAB(Icon入門講座(18))を動かすとどうも 結果がオカシくなります。 調べてみますと、IYABは、拡張子無しの file名を指定するとおかしくなりま す。 更に調べると、iyab.icnは、wild.icnを linkしていますが、wild.icnで既に オカシイ。 あれれ、wild.icnは、拡張子なしの file名の手当をしていたはず だか??? と、思って、良くみると、あれま、ミスコーディング をしていま す。 if not upto('.',line) then line ||= "." というミスをやらかしていました。 つう、ことで、修正版です。 -----^ WILD.ICN ( date:97-05-10 time:00:42 ) --------------->" || tmp) # "dir"発行 } close(dir) } default : { # file指定 if not upto('.',arg) then arg ||:= "." # Rev.1.4 system("dir " || arg || " >>" || tmp) # "dir"発行 } } # end of case } # end of every } # end of else dir := open(tmp) | stop("cannot open " || tmp) # fileオープン # "dir" の結果格納 fileから、file指定部分を 抜き出して listへ。 while line := read(dir) do # tmp fileから1行づつ読込。 put(fn_list,dir2lst(line)) # file情報を抜き出して、 # listへ格納 close(dir) remove(tmp) # 証拠隠滅 #################### # command解析終了 #################### if *fn_list = 0 then fail # 該当 file無し else return fn_list end -----$ WILD.ICN ( lines:65 words:298 ) --------------------- wild.rst # Rev.1.2 1997/05/05 windy 拡張子無しのテストケース追加 # Rev.1.1 1996/12/01 windy # This file is in the public domain. link wild, show_l procedure main() write("test for wild.icn\n") # "abc" という名前のファイルは存在しないこと。 write((L := ["abc"])[1]) show_wl(wild(L)) | write(L[1],"は見つからない。\n") # wild card write((L := ["*.*"])[1]) show_wl(wild(L)) write((L := ["*.icn"])[1]) show_wl(wild(L)) write((L := ["wi*.*"])[1]) show_wl(wild(L)) # response file write((L := ["@WILD_FL.DAT"])[1]) show_wl(wild(L)) # 複数指定 every writes(" ",!(L := ["*.icn","*.dat"])) ; write() show_wl(wild(L)) # "AAA", "AAA.TXT" の 2つの fileを用意しておくこと。 every writes(" ",!(L := ["AAA"])) ; write() # 追加 Rev.1.2 show_wl(wild(L)) write((L := ["wild~.*"])[1]) show_wl(wild(L)) end -----$ WILD~.ICN ( lines:39 words:102 ) -------------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_306.txt 1997/05/10 PCVAN PIG) ■ Icon > Icon散歩道(7) 再び wild.icn(2) 風つかい PIG広場の方で、fileの名前と拡張子を 入れ換える 話題が 出ています。 そのネタをいただいて、Iconでやったらということで、programを 作ってみま した。 Iconには、fileの rename関数があります。 wild.icnで、該当 fileを 探して rename関数で、renameするという programです。 この programでは、log.* ファイルの拡張子とファイル名を入れ換え、その後 もう1度、入れ換えて、元に戻しています。 -----^ REN01.ICN ( date:97-05-10 time:00:37 ) --------------拡張子") every LL := !L do { s := top_cut("\\",LL[1]) # 現file名から directory部を 削除 # ↑file名 LLL := split(s,".") # file名を "."で分割して listへ write(s," -> ",s2 := LLL[2] || "." || LLL[1]) # 動作モニター #↑拡張子 ↑file名 rename(s,s2) # rename } return end -----$ REN01.ICN ( lines:28 words:109 ) --------------------拡張子 LOG.97 -> 97.LOG LOG.95 -> 95.LOG LOG.96 -> 96.LOG file名<->拡張子 95.LOG -> LOG.95 96.LOG -> LOG.96 97.LOG -> LOG.97 -----$ REN01 ( lines:10 words:20 ) ------------------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_307.txt 1997/05/10 PCVAN PIG) ■ Icon > Icon散歩道(8) 再び dir2tree 風つかい ミスが多い と言いつつ、相変わらず、やっていますね〜。 再び wild.icn(7)-> 再び wild.icn(2) ですね。 (このテキストでは直っています。) ところで、Icon入門講座シリーズを、アリゾナ大学の Icon-WEBに 置いて頂く ように、Icon入門講座 (ICON_LEC.LZH)と Icon日記 (ICONLEC2.LZH) の fileを Icon Projectに、送ってお願いしていたんですが、置いて頂けました。場所は、 ftp://ftp.cs.arizona.edu/icon/contrib/Japanese/ です。 /icon/contrib/ は 寄贈 program (contribution)の directoryです。 ここに、Japaneseという directoryを作っていただきました。 英文なら E-mailか FTPで、送ればいいんですが、日本語の SHIFT-JISの file ですので、printしたものと、FDを送ってみました。 とても、全文を英訳する力は、ありませんので、 「 printed materialの programの部分で 内容は 想像して下さい。」 と お願いした文章を、四苦八苦して 書きました。(汗) 近ごろ、カタカナ部分を なるべく 英語で書いていますが それは Icon Project で、内容を 想像しやすいように ということで、やっています。(あはは) 内容は、こちらにアップしたものと 殆ど同じです。(その後 みつかった文章 や綴り(特に英語)の誤記を多少 修正しています。program自体は同じです。) さて、今回は、Icon雑記帳(20)〜(22)で、やりました dir2tree の修 正です。 dir2treeは、私のHDDの Icon関係 directoryの中の sub-directoryで test しました。 で 先日、Icon雑記帳の fileを、Icon-Projectの送ろうと そのFD の directory treeを、表示しようとしましたら、programが、コケました。 root directoryじゃ、動かなひ〜。 dir /s の command を 実行したときに、 ・root directoryでは A:\とか directory名称の後ろに、\ が、つきますが ・sub-directoryでは、つきません。 \ がつく caseの手当を忘れていました。つうことで、その点を修正しました。 -----^ D2TREE.ICN ( date:97-05-10 time:00:48 ) ------------- 0 then args[1] else "" tmp := "$$$tmp.$$$" # テンポラリファイル名設定 # ↓環境変数に、テンポラリーディレクトリ名が、設定してあればファイル名 # の前に追加 if s := getenv("TMP") then tmp := s || "\\" || tmp # コマンドラインの ディレクトリー名指定 をもとに、"dir /s" コマンド を # 発行して tmpへ出力 system("dir /s " || dir_name || " >" || tmp) # directory data sample ↓こんなデータが tmpに格納されます。 #1 16 #ディレクトリは D:\ICON\LEC24\TEST_A # #. 97-04-29 21:51 . #.. 97-04-29 21:51 .. #TEST_B 97-04-29 21:52 TEST_B #BBB TXT 10 97-04-29 21:56 BBB.TXT #TEST_CCC TXT 15 97-04-29 21:57 TEST_CCC.TXT #TEST_C 97-04-29 21:55 TEST_C #AAA 6 97-04-29 21:56 AAA # 3 個 31 バイトのファイルがあります # #一覧のファイル総数: ###################### # dir /s (dir_name)結果から、directory毎の情報を listへ 登録 ###################### dir := open(tmp) | stop("cannot open " || tmp) # tmpファイルオープン L_dir := [] # directory情報登録用 list T_dir := table() # sub-directory list参照 table while line := read(dir) do { # directory情報を directory毎に、一旦 list(LL)にまとめて、登録する。 line ? { #↓listにまとめた directory情報を登録 if find("個") then put(\T_dir[c_dir] | L_dir,LL) #↑tableに c_dirが存在すれば if match(" ") then next # ごみ行を無視 if match("一") then break # "一覧の..."が来たら終わり L := p_split() # lineを " "で分割→[1]:開始位置 # [2]:要素 #↓lineの1番目の要素 case L[1][2] of { "ディレクトリは" : { LL := [] # directory data buffer # ↓lineの2番目の要素の開始位置 if line[-1] == "\\" then c_dir := line[L[2][1] : -1] # root対応 else c_dir := line[L[2][1] : 0] # put(LL,[c_dir]) #↑listにして LLに追加 } "." : next # 無視 ".." : put(LL[1],line) # その directory自身の情報。 # トップの directory以外では必要 # ないが、全てにつき格納 default : { if find("") # sub-directoryだったら then { put(LL,LLL := [line]) # 内容を bufferに追加 insert(T_dir,c_dir || "\\" || L[1][2] ,LLL) # ↑sub-directory名称を keyにして tableへ登録 } else put(LL,line) # 通常 fileならそのまま追加 } } # end of case } # end of line ? } # end of while close(dir) remove(tmp) # delete tmp ####################### # directory情報の表示 ####################### # every write(x2t(L_dir) \25) # list 内容確認用表示 # every write(x2t(T_dir) \25) # table内容確認用表示 # stop() every write(d2t(L_dir)) end ######################## # directory tree表示 ######################## procedure d2t(L) # top directoryの名前/time_stamp処理 L1 := get(L) # Lには1個しか要素が無い。それを取り出す。 L2 := get(L1) # そのまた top (directory_name,time_stamp)を取り出す。 dir_top := L2[2][14:-3] | "\\ " # top directoryの名前と time stamp ↓root directory対応 suspend L2[1] || dir_top suspend " " || d2tree(L1) # tree化処理 # ↑tree書出し初期位置 end procedure d2tree(L) # directoryの処理 # show the tree structure of directory n := *L # Lの size(末尾要素検出用) every LL := !L do { # listの要素を1つづつ取り出す n -:= 1 # 要素カウンタ−1 case t := type(LL) of { # 要素の typeにより、 "string" : suspend "+ " || LL # file "list" : { # sub-directory suspend "+ " || get(LL) # sub-directory自体表示 LLL := get(LL) # sub-directoryの中身を取り出し get(LLL) # 先頭要素を読み飛ばし #↓最後の要素でないなら suspend (if n > 0 then "| " else " ") || d2tree(LLL) } default : stop("error") } } end ######################## # stringを cを区切りにして分割して、開始位置と要素を返す。 ######################## # 動作は、Icon入門講座(7) Iconの特徴(5)スキャンを参考にして下さい。 procedure p_split(line,c) /c := ' \t' # default /line := &subject # default list := [] # 戻り値用 list line ? { while tab(ps := upto(~c)) do put(list,[ps,tab(pe := many(~c))]) } return list end -----$ D2TREE.ICN ( lines:141 words:544 ) ------------------ BGM: 河よりも長くゆるやかに/篠原美也子 (icon_308.txt 1997/05/11 PCVAN PIG) ■ Icon > Icon散歩道(9) 再び x2tree(1) 風つかい まず、前回の 「再び dir2tree」の続きです。 FDの root directoryからの treeを 表示してみました。 -----^ ICON_FD2.DIR ( date:97-05-10 time:23:55 ) ----------- + NOT_ARCH 97-05-03 21:00 NOT_ARCH | + ICON_LEC DOC 3,038 97-04-23 23:17 ICON_LEC.DOC | + ICON_LEC TXT 116,916 97-04-23 23:15 ICON_LEC.TXT | + ICONLEC2 DOC 2,686 97-04-23 23:19 ICONLEC2.DOC | + ICONLEC2 TXT 125,700 97-04-23 23:20 ICONLEC2.TXT | + ICONLEC3 DOC 2,746 97-05-08 12:47 ICONLEC3.DOC | + ICONLEC3 TXT 105,141 97-05-08 12:47 ICONLEC3.TXT + OTHER 97-05-03 21:00 OTHER | + ICON_LEC ENG 3,109 97-04-23 23:30 ICON_LEC.ENG | + ICONLEC2 ENG 2,869 97-04-23 23:32 ICONLEC2.ENG | + ICONLEC3 ENG 2,924 97-05-09 20:00 ICONLEC3.ENG | + CONTRIB TXT 2,309 97-04-26 13:57 CONTRIB.TXT | + CONTRIB2 TXT 752 97-05-09 19:39 CONTRIB2.TXT + ICON_LEC LZH 42,239 97-04-26 10:07 ICON_LEC.LZH + ICONLEC2 LZH 44,737 97-04-26 10:07 ICONLEC2.LZH + ICONLEC3 LZH 33,723 97-05-08 12:48 ICONLEC3.LZH -----$ ICON_FD2.DIR ( lines:17 words:123 ) -----------------" #↑listの serial No ss := repl(" ", \step | 0) suspend " " || ss || x2tree(x,ss,"") #↑初期化フラグ end procedure x2tree(x,ss,init) # generator # show the tree structure of x static T # serial No 登録用 set登録用 table static宣言をして # おかないと、再帰したときに、未定義 errorとなる。 if \init then { # 初期化を initialでやると、同じ program内で別の # 構造体 tree表示のために、再び x2tを 呼んだ時に # 実行されなくて状態が残るので、フラグで初期化指示 T := table() T["list"] := set() # ここは、table(set())とすると、全て同じ setが T["set"] := set() # 割り当てられてしまうので、個別に初期化する。 T["table"] := set() if (t := type(x)) == ("list" | "set" | "table") then insert(T[t],serial(x)) # serial Noを T[type(x)]に登録 } n := *x # 枝の末端検出用カウンタ if (tt := type(x)) ~== "table" then { # not table every xx := !x do { # 構造体の要素を1つ づつ取り出す n -:= 1 t := type(xx) val_disp := if t == ("list" | "set" | "table") then "+ <" || t || " " || string(m := serial(xx)) || ">" else "+ " || left(t,7) || jimage(xx) case t of { # 構造体のタイプにより、 "string" : suspend val_disp #↓list,set,tableなら、typeを書出して、その中身をまた解析する。 "list" | "set" | "table" : { #↓再帰構造で なければ。(T[type(xx)] に既に登録済みなら再帰構造) if not member(T[t],m) then { insert(T[t],m) # serial Noを T[type(xx)]へ登録 suspend val_disp suspend (if n > 0 then "| " else " ") || ss || ss || x2tree(xx,ss) #↑枝の途中 #↑枝の末端 # 再帰↑ delete(T[t],m) #←serial No 登録を削除 } else { # 再帰構造 ならば、書き出すとキリが無いので打止め return val_disp || " (Recursion)" } } #↓その他なら、typeと値を書き出す。 default : suspend val_disp } } } else { # table every xkey := key(x) do { # tableの keyを1つ づつ取り出す n -:= 1 tk := type(xkey) xx := x[xkey] t := type(xx) val_disp := if t == ("list" | "set" | "table") then "+ <" || t || " " || string(m := serial(xx)) || ">" else "+ " || left(t,7) || jimage(xx) key_disp := if tk == ("list" | "set" | "table") then " <- <" || tk || " " || string(serial(xkey)) || ">" else " <- " || left(tk,7) || jimage(xkey) case t of { # 構造体のタイプにより、 "string" : suspend val_disp || key_disp #↓list,set,tableなら、typeを書出して、その中身をまた解析する。 "list" | "set" | "table" : { #↓再帰構造で なければ。(T[type(xx)] に既に登録済みなら再帰構造) if not member(T[t],m) then { insert(T[t],m) # serial Noを T[type(xx)]へ登録 suspend val_disp || key_disp suspend (if n > 0 then "| " else " ") || ss || ss || x2tree(xx,ss) #↑枝の途中 #↑枝の末端 # 再帰↑ delete(T[t],m) #←serial No 登録を削除 } else { # 再帰構造 ならば、書き出すとキリが無いので打止め return val_disp || key_disp || " (Recursion)" } } #↓その他なら、typeと値を書き出す。 default : suspend val_disp || key_disp } } } end -----$ X2TREE07.ICN ( lines:101 words:448 ) ---------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_309.txt 1997/05/11 PCVAN PIG) ■ Icon > Icon散歩道(10) 再び x2tree(2) 風つかい 前回の 「再び x2tree」の続きです。test programとその結果です。 test programが長くなりましたので、分けました。 -----^ X2TREE7~.ICN ( date:97-05-08 time:21:54 ) ----------- + string "A" + string "B" + | + string "C" | + string "D" | + | + string "E" | + string "F" | + (Recursion) + + string "E" + string "F" + (Recursion)
+ string "C" <- string "C" + <- string "E" | + string "E" | + string "F" | + | | + string "A" | | + string "B" | | + | | | + string "C" | | | + string "D" | | | + (Recursion) | | + (Recursion) | +
(Recursion) + string "A" <- + string "G" <- string "A" + string "K" + | + string "H" | + string "I" + | + string "M" | + string "L" | + (Recursion) + string "J" + string "A" + string "B" + | + string "C" | + string "D" | + | + string "E" | + string "F" | + (Recursion) + + string "E" + string "F" + (Recursion) + string "K" + | + string "H" | + string "I" + | + string "M" | + string "L" | + (Recursion) + string "J"
+ string "C" <- string "C" + <- string "E" | + string "E" | + string "F" | + | | + string "A" | | + string "B" | | + | | | + string "C" | | | + string "D" | | | + (Recursion) | | + (Recursion) | +
(Recursion) + string "A" <- + string "G" <- string "A" + string "A" + string "B" + | + string "C" | + string "D" | + | + string "E" | + string "F" | + (Recursion) + + string "E" + string "F" + (Recursion) + string "K" + | + string "H" | + string "I" + | + string "M" | + string "L" | + (Recursion) + string "J"
+ string "C" <- string "C" + <- string "E" | + string "E" | + string "F" | + | | + string "A" | | + string "B" | | + | | | + string "C" | | | + string "D" | | | + (Recursion) | | + (Recursion) | +
(Recursion) + string "A" <- + string "G" <- string "A" -----$ X2TREE07 ( lines:124 words:501 ) -------------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_310.txt 1997/05/11 PCVAN PIG) ■ Icon > Icon迷い道(1) system() 風つかい Icon散歩道の方では、次は 多項式の展開 をやってみるつもりなんですが、 結構 道が険しくて、次のお話しが書けません。 (a+b+c)^2 を a^2+b^2+c^2+2ab+2ac+2bcに、変換する程度の処理を、考えて いるのですが、どうやって programを 組めば良いのか 思いつきません。 そちらは、しばらく考えてみるとして、何にも書かないのもさみしいので、 近ごろ書いた programを紹介します。 niftyと PCVANの log fileから OSL/Libraryの Icon関係 fileタイトルの部分 を抜き出すものです。 YOTさんの ygrepを使わさせて頂いています。 ygrepの引数になる文字列を作って、system()関数に与えるだけです。 MS-DOSの batch programで書ける内容ですが、re-directが できないとかの 制限がありますので、Iconから呼んでいます。 command line引数に、日付(月日)を与えると その日付の log fileを、 与えなければ、その日の log fileを検索します。 map関数を使って、 keywordの &dateから日付を 抜き出しています。 &dateは、1997/06/04という形式をしています。これから 0604の部分を 抜き出すものです。 すると、使っている式は map("679A","123456789A","1997/06/04") となります。 map関数は、本来は文字変換の関数です。 その解釈ですと、前の式は 文字列 "679A"を 次の 規則で変換することになります。 1 -> 1 すると、ご覧のように "679A"は、ちょうど月日の部分 2 -> 9 の数字に変換されます。 3 -> 9 ちょっと、パズルみたいですが、このやり方を 覚えて 4 -> 7 おくと便利です。 5 -> / 部分文字列指定の 6 -> 0 &date[[6:8] || &date[9:0] 7 -> 6 と同じ結果になります。 8 -> / 9 -> 0 A -> 4 さて、 programです。 -----^ IDLG.ICN ( date:97-06-04 time:23:49 ) --------------- idlg.icn # comment追加 # idl.icn Rev.1.2 1997/06/01 windy 不正 date検出強化 # idl.icn Rev.1.1 1997/05/23 windy Kazetuskai H.S. # YOTさんの ygrep(超高速・多機能 grep)を使用。 # Usage: idlg または idlg MMDD procedure main(args) # 日付指定が あればその日付 無ければその日 の log fileを対象にする。 # ↓format変換(月日抜き出し) date := right(numeric(args[1]),4,"0") | map("679A","123456789A",&date) # 1997/06/04の形式↑ # ygrepの呼び出しパラメータ生成 # nifty用 ↓ OR検索 s1 := "ygrep \"{icon,bipl,de386}.*lzh\"" || " nif" || date || ".log " # PCVAN用 ↓一致行の前の行も出力 s2 := "ygrep -B1 \"{icon,bipl,de386}.*lzh\"" || " van" || date || ".log " write(&errout,s1) system(s1) # nifty log検索 write(&errout,s2) system(s2) # PCVAN log検索 end -----$ IDLG.ICN ( lines:26 words:105 ) --------------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_351.txt 1997/06/06 PCVAN PIG) ■ Icon > Icon迷い道(2)download数 集計 風つかい 前回、Iconから ygrepを呼んで、log fileから Icon関係 fileのタイトル行を 切り出す programをご覧にいれました。 更に 集計表まで 出してくれるといい ということで、Iconだけで集計 programを書いてみました。 -----^ DL970607 ( date:97-06-07 time:00:35 ) --------------- PIG SLABO PFL FGALTS file計 BIPL.LZH - 11 - 19 30 DE386.LZH - 12 - 40 52 ICONLEC3.LZH 3 11 3 46 63 ICONLEC2.LZH 3 16 41 67 127 ICON_LEC.LZH 6 32 91 134 263 ------------------------------------------------ SIG/forum計 12 82 135 306 535 -----$ DL970607 ( lines:8 words:43 ) ----------------------- idl.icn # comment追加 # idl2.icn Rev.1.1 1997/06/04 windy Kazetsukai H.S. # This file is in the public domain. # Usage: idl または idl MMDD procedure main(args) # download数 格納 list(2重 list) #↓ [1]:PIG [2]:SLABO [3]:FPL [4]:FGALTS L := [ [ 0 ,0, 0 ,0], # L[1] ICON_LEC.LZH [ 0 ,0, 0 ,0], # L[2] ICONLEC2.LZH [ 0 ,0, 0 ,0], # L[3] ICONLEC3.LZH ["-",0,"-",0], # L[4] DE386.LZH PIG,FPLには uploadしていない。 ["-",0,"-",0] ] # L[5] BIPL.LZH PIG,FPLには uploadしていない。 # file名称 作成 # command line引数が数字に変換できれば、file日付に使う。できなければ、 # その日の日付を file日付に使う。 f_date := right(numeric(args[1]),4,"0") | map("679A","123456789A",&date) #↑ &dateの 6,7,9,10番目の文字を抜き出す。(map関数の応用) # &dateは 1997/06/04の形式 f_nif := "nif" || f_date || ".log " # nifty log file名生成 f_van := "van" || f_date || ".log " # PCVAN log file名生成 #################### # nifty 処理 #################### # nifty log file例 #1 16 33 37 # 17 PFF01531 96/12/08 41893 130 B ICON_LEC.LZH テキスト処理言語Icon入門講座 dir_nif := open(f_nif) | stop("cannot open ",f_nif) write(&errout,f_nif) size_nif := 15 # Icon関係 file検出のための照合字数 while line := read(dir_nif) do { # log fileを1行づつ読み込んで nn := numeric(line[33:37]) # download数 切り出し case line[1:size_nif+1] of { # download数を listに登録 " 17 PFF01531 " : L[1][4] := nn # FGALTS ICON_LEC.LZH " 20 PFF01531 " : L[2][4] := nn # FGALTS ICONLEC2.LZH " 21 PFF01531 " : L[3][4] := nn # FGALTS ICONLEC3.LZH " 163 PFF01531 " : L[4][4] := nn # FGALTS DE386_93.LZH " 124 PFF01531 " : L[5][4] := nn # FGALTS BIPL93.LZH " 78 PFF01531 " : L[1][3] := nn # FPL ICONLEC1.LZH " 81 PFF01531 " : L[2][3] := nn # FPL ICONLEC2.LZH " 82 PFF01531 " : L[3][3] := nn # FPL ICONLEC3.LZH } } close(dir_nif) #################### # PCVAN処理 #################### # PCVAN log file 例 #1 19 52 59 # 219.ICON_LEC.DOC TRA11936 96/12/ 8 0002421 0000032 # ICON_LEC.LZH TRA11936 96/12/ 8 0041893 0000032 dir_van := open(f_van) | stop("cannot open ",f_van) write(&errout,f_van) size_van := 18 # Icon関係 file検出のための照合字数 while line := read(dir_van) do { # log fileを1行づつ読み込んで nn := numeric(line[52:59]) # download数 切り出し case line[1:size_van+1] of { # SIG検出 (" 219.ICON_LEC.DOC " | " 243.ICONLEC2.DOC " | " 256.ICONLEC3.DOC " ) : mode := "LAB" # SLABO処理中 (" 79.ICON_LEC.DOC " | " 84.ICONLEC2.DOC " | " 89.ICONLEC3.DOC ") : mode := "PIG" # PIG処理中 # download数を listに登録 " ICON_LEC.LZH " : if mode == "LAB" then L[1][2] := nn # SLABO else L[1][1] := (nn-1) # PIG # download test分 ↑ " ICONLEC2.LZH " : if mode == "LAB" then L[2][2] := nn # SLABO else L[2][1] := nn # PIG " ICONLEC3.LZH " : if mode == "LAB" then L[3][2] := nn # SLABO else L[3][1] := nn # PIG " DE386_93.LZH " : L[4][2] := nn # SLABO " BIPL93.LZH " : L[5][2] := nn # SLABO } } close(dir_van) #################### # 表にして出力 #################### f_name := [ "ICON_LEC.LZH ", # file名称 list "ICONLEC2.LZH ", "ICONLEC3.LZH ", "DE386.LZH ", "BIPL.LZH ", "SIG/forum計 " ] size_f_name := *f_name[1] sig := [ " PIG", # SIG/forum名称 list " SLABO", " PFL", " FGALTS", " file計" ] size_sig := *sig[1] # 日付生成 ↓fdateに"/"を挿入。(map関数の応用) writes(left("<" || &date[1:6] || map("12/34",1234,f_date) || ">",size_f_name)) every writes(!sig) # SIG/forum名出力 write() L_sum_col := [0,0,0,0,0] # SIG/forum毎の download数 集計用 list every i := *L to 1 by -1 do { # listの末尾から(逆順出力のため) writes(f_name[i]) # file名出力 sum_line := 0 # file毎の download数 集計 every j := 1 to *L[i] do { writes(right((nnn := L[i][j]),size_sig)) # file毎 download数 sum_line +:= (nnnn := numeric(nnn) | 0) # 数字なら足し込む L_sum_col[j] +:= nnnn # 数字なら足し込む } write(right(sum_line,size_sig)) # file毎 daownload数 出力 L_sum_col[-1] +:= sum_line # 総計に足し込む } write(repl("-",*L_sum_col * size_sig +size_f_name)) # 仕切り線 writes(f_name[-1]) # "SIG/forum計 "の文字を出力 # (f_nameの最後の要素) every writes(right(!L_sum_col,size_sig)) # SIG/forum毎の計 write() end -----$ IDL.ICN ( lines:131 words:617 ) --------------------- PIG SLABO FPL FGALTS file計 BIPL.LZH - 14 - 33 47 DE386.LZH - 17 - 89 106 ICONLEC3.LZH 3 12 18 95 128 ICONLEC2.LZH 5 16 56 111 188 ICON_LEC.LZH 7 34 109 196 346 ------------------------------------------------ SIG/forum計 15 93 183 524 815 -----$ DL971124 ( lines:8 words:43 ) ----------------------- BGM: 河よりも長くゆるやかに/篠原美也子 (icon_352.txt 1997/06/07 PCVAN PIG) ■ Icon > Icon迷い道(3)CSV:定義ファイルの読込 風つかい 夏の盛りから、担当している 某 projectの関係で、某所に拉致されており ましたが、一区切りつきましたので娑婆に戻って、気がつけばもう秋深し。 Icon講座は、どこまで書いたか記憶の彼方です。 確か、CSVデータ(コンマ区切りデータ)を、 表形式の テキストデータに 変換するprogram を作って upload しようとしていた ような 気がします。 リハビリと兼ねて、思い出しながら 続きをやってみたい と思います。 しかし、某 projectは未だ続いていますので、Icon講座が 途絶えましたら、 また 某所に拉致されたな と思って下さい。(笑) さて、CSVデータというのは、こんなデータです。 "なかやまみほ","中山美穂",1970,3,1,"O",158,80,60,84,45,"東京都小金井市" コンマ区切りのデータで、文字列は " で くくってあります。 文字列中に " が 現れる場合は、"" で表わすものとします。 CSVといっても、細かい点で仕様が違うものが、いくつかあるようですが、ここ ではこの仕様とします。 例のようなデータが並んでいる データファイルを、表形式のデータへ 変換した り、表形式のデータを逆に CSVデータへ変換するものを作ってみます。 例のデータを表形式にするためには、各欄を何文字の表にするかの情報をどこか で定義しないといけません。 たとえばこんな感じです。各欄の文字数を定義する ついでに、文字か数字かの情報もまとめて定義しています。 -----^ CSV2TB.CFG ( date:97-10-27 time:00:02 ) ------------- 表データ 変換指定ファイル #################### # 行頭が "#"あるいは行に "="が含まれ無いなら、無視。(コメント) # 処理対象ファイル名の先頭の文字にて、処理指定を識別する。 # ファイル識別文字("="の手前の文字)は、英字大文字。 # 変換指示は、","区切りのデータを何桁のデータとして扱うかを、指示。 # 変換指定例 # s14 : 14桁の文字列、n4 : 4桁の数字、s0 : その欄は無視(n0でも良い) # csvデータより、指定桁が大きい場合は、後ろに " "が追加される。 # 変換指定が無い部分の csvデータは無視される。 #################### # テスト用サンプルデータ # "なかやまみほ","中山美穂",1970,3,1,"O",158,80,60,84,45,"東京都小金井市",10 SAM= s13 s9 n4 n2n2 s0 n3 n3 n3 n3 n3 s12 n2 -----$ CSV2TB.CFG ( lines:17 words:52 ) -------------------- + string "ABC" + string "abc_ini" + string "DEFG" + string "デーイーエフジー" + string "hijk" + string "123,456" + string "hijk" + string "123,456,789" + string "lmno" + string "lmnoだ" + string "pqrs" + string "" + string "lmn" + string "lmnだ" lmnoだ lmnoだ lmnだ error -----$ GET_INIL ( lines:20 words:50 ) ---------------------- BGM: Smile/岡本真夜 (icon_353.txt 1997/10/27 PCVAN PIG) ■ Icon > Icon迷い道(4)CSV:定義ファイルの解析 風つかい 今日は、定義ファイルの解析の部分をやります。定義ファイルは、CSVデータと 並べて次のように指定したいのです。 SAM= s4 s1 n1n2 n3 s16 # "地球","E",1,15,256,"ちきゅうはまるい" 順に、地球 -> s4 E -> s1 1 -> n1 15 -> n2 256 -> n3 ちきゅうはまるい -> s16 と指定している訳です。 programでは、CSVデータの欄に対応した指定を listに格納しておくことに します。 前回の定義ファイルの読込みでは、 SAM= s4 s1 n1n2 n3 s16 という stirngを ["SAM"," s4 s1 n1n2 n3 s16"] と 変換して、"SAM"で listデータを サーチして、" s4 s1 n1n2 n3 s16" という文字列を 取り出すところまで 済んでいます。 今回は、この指定文字列を、欄毎の指定が順に詰まった listへ変換する部分 を作ります。 指定が全てスペース区切りなら、以前紹介しました splitとかで listに変換 できるのですが、今回のデータは スペースが無い部分もあります ので、 別の 方法が必要です。 指定が、アルファベット1文字と数字の組み合わせですので、その組み合わせ を検索して、listに順に格納する procedureを作ります。 こういう データ変換 procedureを 簡単に作れるところが、Iconの良いところ です。 というところで、programです。 Test programも一緒に入れています。 -----^ CN_SPLIT.ICN ( date:97-10-29 time:00:42 ) ----------- [s4,s1,n1,n2,n3,s16] と変換 # "地球","E",1,15,256,"ちきゅうはまるい" # 備考 : csvデータを表データに変換するプログラムのために作成 # 上の例の用に、データ例と並べて指定が可能なプログラムにするため procedure cn_split(s,c1,c2) /s := &subject # default /c1 := &letters # 英文字の大文字・小文字のセット /c2 := &digits # 数字のセット '0123456789' L := [] # 戻り値用 list s ? { while tab(upto(c1)) do { # c1迄スキップ ss := "" # ss := tab(many(c1)) # c1以外の文字迄スキップし、その間の文字 # を ssへ put(L,ss ||:= tab(many(c2))) # c2以外の文字迄スキップし、その間の文字 # を ssへ足しこむ } } return L end -----$ CN_SPLIT.ICN ( lines:42 words:140 ) ----------------- BGM: Smile/岡本真夜 (icon_354.txt 1997/10/29 PCVAN PIG) ■ Icon > Icon迷い道(5)CSV:拡張子変更(1) 風つかい さて、今日は 拡張子を変更する procedureについて説明します。この procedure は、CSVデータのファイル名を foo.csvとしますと、CSVデータを表形式データに変換 したデータのファイルは拡張子を変えて、foo.txtという名称にするためのものです。 まず ファイル名の '.' の前の部分と 後ろの部分を分ける procedureを作ります。 早速、programです。 -----^ DOT.ICN ( date:97-10-30 time:00:33 ) ---------------- BGM: Blow by Blow / Jeff Beck (icon_355.txt 1997/10/30 PCVAN PIG) ■ Icon > Icon迷い道(6)CSV:拡張子変更(2) 風つかい さて、拡張子を変更する procedureのつづきです。 前回 作りました '.' の前の 部分と 後ろの部分を分ける procedureを使って、拡張子を変更したりチェックした りする procedureを作ります。 -----^ EXT.ICN ( date:97-10-30 time:00:48 ) ---------------- BGM: Blow by Blow / Jeff Beck (icon_356.txt 1997/10/30 PCVAN PIG) ■ Icon > Icon迷い道(7)CSV:CSV2TB 風つかい さて、CSVデータを表形式データへ変換する programのまとめです。 CSVデータのファイルから、1行ずつデータを読込んで、CSVデータを表形式データ に変換して、書き出していきます。 -----^ CSV2TB.ICN ( date:97-10-26 time:21:08 ) ------------- ",f_new) # 動作モニター # display form data # every writes(&errout," ",!form) ; write(&errout) # main no := 0 # モニター用表示の行番号初期値 while line := read(intext) do { # ファイルを1行ずつ読み込み、 write(outtext,csv2tb(line,form)) # form指定に従い変換 writes(&errout," ",right(no +:= 1,4)) # モニター用表示 } write(&errout,"") # カーサー復帰 # close files (無くても動く) close(intext) close(outtext) end #################### # csvデータ1行変換 procedure #################### # args : [1]: string csvデータ(1行)、[2]: list 変換指定文字+桁の list、 # [3]: 桁揃え追加文字 # value: string 表形式に変換された文字列(1行) procedure csv2tb(line,form,s_fill) /s_fill := " " # 指定が無ければ、spaceと見なす。 # 原データ中の spaceと桁揃えのために付加する spaceを区別を # する時使用。例えば、データ中の " "を "_"とかに変換する。 if line[1] == "#" then { # 行の先頭が "#"ならコメント行 return line } l_list := split_csv(line) # 行データを、","区切りで、listに変換。 ################## # 指定桁合わせ ################## i := 1 # 欄番号 s_line := "" # 文字列足し込み用の空文字列 while i <= *form do { # 変換指定の数分、ループ if not (form[i][2:0] == "0") then { # 0桁指定でなければ if form[i][1] == "s" # 文字列指定だったら、左詰め then s_line ||:= (left(map(string(l_list[i])," ",s_fill), form[i][2:0]) || " ") # 数字指定だったら、右詰め else s_line ||:= (right(l_list[i],form[i][2:0]) || " ") } i +:= 1 } return s_line[1:-1] # 末尾の spaceの つけ過ぎを削除。 end #################### # csvデータを listに変換 #################### # args : string csvデータ(1行) # 文字列データは 両端を \" で囲まれているものとする。 # 文字列中の \"は、\"\"で表わされているものとする。 # value: list 欄データを順に listに詰めたもの # 備考 : 3状態の状態遷移マシン state : "init": 初期状態, # "string":文字列処理状態, "number":数字処理状態 procedure split_csv(line) state := "init" # 動作状態管理変数に、初期状態をセット # "string":文字列処理状態, "number":数字処理状態 L := [] # 戻り値用 空 list line ? { repeat { # write(&errout,&pos," ",state) case state of { "init" : { # 初期状態 buf := "" # 文字列足し込み用バッファー初期化 s := move(1) | fail # 次の1文字を get if s == "\"" # stringデータの始め記号なら then state := "string" # 状態 -> 文字列処理 else { state := "number" # 状態 -> 数字処理 move(-1) # &pos戻し } } "number" : { # 数字データ処理状態 put(L,tab(upto(',') | 0)) # 次の ","迄の文字列を list Lに追加 move(1) | break # skip "," または行末でループを抜ける。 state := "init" # 状態 -> 初期状態 } "string" : { # 文字列データ処理状態 buf ||:= tab(upto("\"")) # 次の "\""迄の文字列を bufに追加 move(1) # skip \"(必ずあるはず。) if (s := move(1)) # 行末チェック then { # 行末でなければ if s == "," # データ区切り記号 then { # 前の \"は、stringの終わりだった。 put(L,buf) # bufの内容を listへ追加 state := "init" # 状態 -> 初期状態 } else { # 前の \"は、escapeだった。 buf ||:= s # bufに足し込み state := "string" # 状態 -> 文字列処理 } } else { # 行末なのでループを抜ける。 put(L,buf) # bufの内容を listへ追加 break } } default : stop("state error") # ここへは、来ないハズ。 } } } return L end -----$ CSV2TB.ICN ( lines:162 words:594 ) ------------------ BGM: Sun & Moon/岡本真夜 (icon_357.txt 1997/10/31 PCVAN PIG) ■ Icon > Icon迷い道(8)CSV:CSV2TB 風つかい さて、今回は、表形式データを CSVデータへ変換する programです。この program でも、定義ファイルを使いますが、次のような形式です。 -----^ TB2CSV.CFG ( date:97-10-26 time:21:20 ) ------------- csvファイル変換指定ファイル #################### # 行頭が "#"あるいは行に "="が含まれ無いなら、無視。(コメント) # 処理対象ファイル名の先頭文字にて、処理指定を識別する。 # ファイル識別文字("="の手前の文字)は、英字大文字。 # 変換指示は、 # s: 文字データ先頭指示、n: 数字データ先頭指示、d: 削除データ先頭指示 #################### # テスト用サンプルデータ #なかやまみほ 中山美穂 1970 3 1 O 158 80 60 84 45 東京都小金井 SAM=s ds dn dn dn dsdn dn dn dn dn ds -----$ TB2CSV.CFG ( lines:13 words:47 ) -------------------- ",f_new) # 動作モニター # display form data # every writes(&errout," ",!form) ; write(&errout) # main no := 0 # モニター用表示の行番号初期値 while line := read(intext) do { # ファイルを1行ずつ読み込み、 write(outtext,tb2csv(line,form)) # form指定に従い変換 writes(&errout," ",right(no +:= 1,4)) # モニター用表示 } write(&errout,"") # カーサー復帰 # close files (無くても動く) close(intext) close(outtext) end #################### # 表データ -> csvデータ1行変換 procedure #################### # args : [1]: string 表データ(1行)、[2]: list 変換指定(2重 list) # value: string csv形式に変換された文字列(1行) procedure tb2csv(s,L) ss := "" every i := 1 to *L do { ss ||:= case (LL := L[i])[1] of { "s" : { "\"" || replace(s[LL[2]:LL[3]],"\"","\"\"") || "\"\," } "n" : { s[LL[2]:LL[3]] || "\,"} # "d" の部分は無視する。 } } return ss[1:-1] end #↑最後の余分な "\,"を削除 -----$ TB2CSV.ICN ( lines:73 words:275 ) ------------------- BGM: Sun & Moon/岡本真夜 (icon_358.txt 1997/10/31 PCVAN PIG) ■ Icon > ちょっと Icon(1)data整形 風つかい (niftyserveの FGALTSに「AWK天国/なんだ簡単じゃねえか!」という会議室が ありますが、そこで data整形の話題が でました。 それをネタに FGALTSの「スクリプト天国/何でもかんでも」の会議室で、 Iconの紹介を したものです。) 次のような dataを -----^ TEST03.DAT ( date:97-05-17 time:13:32 ) -------------kekka # data sample(/line) : 1,"A" # This file is in the public domain. BEGIN{ FS = "," # 行内の data区切りを ","に変更 No = 0 # データ種別番号 n = 1 # (同一データ種別番号の)データ個数カウンタ N = 3 # 区切り数 } { if($1 != No){ # data種別 Noが変わったら No = $1 # data種別 No更新 if(n % N != 1 ){ print ""} # top dataで無ければ、改行出力 n = 1 # data個数 reset } if(n % N == 1) { # N個 区切りの先頭ならデータ種別 No出力 # printf($1) # printf($1 FS $2) # Rev1.2 } # printf(FS $2) # printf(FS $3 FS $4) # Rev1.2 if(n % N == 0) { print ""} # N個 区切り数最後なら、改行出力 n++ # data数カウントアップ } END{ if(n % N != 1) { print ""} # 最後の行が N個でなければ、改行出力 } -----$ SHAPE01.AWK ( lines:33 words:147 ) ------------------kekka # data sample(/line) : 1,"A" # shape01.awk を、そのまま、Iconに移し変えたもの。 # This file is in the public domain. link split6 procedure main(args) # ↑command line 引数 (list) Usage := "shape01 data_file >kekka" # command line 引数チェック(引数が無ければ使用方法表示) if *args = 0 then stop(Usage) # data fileオープン(エラーならエラー表示) dir := open(args[1]) | stop("cannot open ",args[1]) # ↑command line引数の1番目 fs := '\,' # 行内の data区切り No := 0 # データ種別番号 n := 1 # (同一データ種別番号の)データ個数カウンタ N := 3 # 区切り数 while line := read(dir) do { # 1行づつ順次読み込んで、 L := split(line,fs) # fsにて dataを分割して、listへ if numeric(L[1]) ~= No then { # データ種別 Noが変わったら if (n % N) ~= 1 then write() # top dataで無ければ、改行出力 No := numeric(L[1]) # データ種別 No更新 n := 1 # data個数 reset } if (n % N) = 1 then writes(No) # N個 区切りの先頭ならデータ種別 No出力 # if (n % N) = 1 then writes(No,fs,L[2]) # writes(fs,L[2]) # データ出力 # writes(fs,L[3],fs,L[4]) # データ出力 if (n % N) = 0 then write() # N個 区切り数最後なら、改行出力 n +:= 1 # data数カウントアップ } if (n % N) ~= 1 then write() # 最後の行が N個でなければ、改行出力 end # 解説 # *args : argsの size(この場合は、引数の個数) # stop(s) : 文字列 sを出力して、programを stopする関数 # read() : text-mode file 読み込み関数 # numeric : 文字→数字変換関数 # = : 数字の比較 # := : 代入 # +:= : n +:= m は、 n := n +m の略記 # ~ : 否定 # writes : printf 相当(改行無し出力) # write : print 相当(改行付き出力) # "abc" : 文字列 abc # 'xyz' : 'x' 'y' 'z' を要素とする集合 # L[3] : L (list)の 3番目の要素 # link : 他の fileの procedureを linkする指定 -----$ SHAPE01.ICN ( lines:57 words:273 ) ------------------ BGM: Vivien/篠原美也子 (nif0516a.txt 1997/05/16 niftyserve fgalts, 1997/05/17 PCVAN SLABO) ■ Icon > ちょっと Icon(2)data整形 風つかい 1行データを 3個づつまとめる データ整形 の お話しの続きです。 前回の programで使っている 文字列分割の procedureです。 -----^ SPLIT6.ICN ( date:97-05-17 time:13:09 ) ------------- BGM: Vivien/篠原美也子 (nif0516b.txt 1997/05/16 niftyserve fgalts, 1997/05/17 PCVAN SLABO) ■ Icon > ちょっと Icon(3)data整形 風つかい 1行データを 3個づつまとめる データ整形 の お話しの続きです。 前の programに Iconの機能を、多少ちりばめてみます。 でも、同じ機能の programですから、動いた結果は同じです。(汗) ・読み込んだ dataの各行の分割のところで、最初の ","で、行データを分割す るプログラムにします。 ・行データを、table(AWKの連想配列)に格納します。 (格納する必要は 無いのですが、tableのサンプルとして。) そうしますと、AWKの連想配列と同じく、取り出すときに、格納した順番とは なりません。 そこで、tableに格納した dataを keyで sortして出力します。 tableは sortすると、listになります。( dataに順番を与えるには、listに しないといけないので、Iconの言語仕様が そうなっています。) ・出力処理で、Iconでは、せっかく case構文が、サポートされています ので、使ってみます。 多少、動作が分かりやすくなると思います。 ・整形結果を 出力した後に、使った tableと listの構造が どうなってい るかを、x2tという procedureを使って書き出します。(これは、参考用 です。本来の動作とは関係ありません。) x2tを説明しますと、 Icon入門講座1〜3全部を、説明しないといけなく なりますので、まあ そういう機能の procedureだと、 ここでは、考えて 下さい。(Icon入門講座3(19)の x2tree03 にミス修正(汗)と機能 アップ(tableの場合 valueだけでなく keyも出力するように) したもの です。) -----^ SHAPE02.ICN ( date:97-05-17 time:10:17 ) ------------kekka # data sample(/line) : 1,"A" # This file is in the public domain. link x2tree06 procedure main(args) Usage := "shape02 data_file >kekka" if *args = 0 then stop(Usage) dir := open(args[1]) | stop("cannot open ",args[1]) fs := '\,' # 行内の data区切り N := 3 # 区切り数 T1 := table() # tableを生成(初期値 無し) while line := read(dir) do { # 1行づつ順次読み込んで、 line ? { # lineを走査対象として ps := upto(fs) # ps : lineの中で、最初に fsが見つかった位置 n := numeric(line[1:ps]) # lineの psより前の部分を数字変換 s := line[ps+1:0] # lineの psより後の部分 /T1[n] := [] # table T1の keyに、nが無ければ、nを key # として valueに [] (空 list)を 登録 put(T1[n],s) # tableの key n の value (list)に sを追加 } } L1 := sort(T1,1) # T1 を keyで sortし、 listへ変換 write("整形結果") every L2 := !L1 do { # L1から、要素(list) を順次取り出して No := L2[1] # 1番目の要素(数字)を Noに代入 n := 0 # 同じ Noの要素のカウンタ every s := !L2[2] do { # 2番目の要素(list) から要素(文字列)を # 順次取り出して n +:= 1 # 同じ Noの要素のカウンタ +1 case (n % N) of { # nを Nで割った余りによって 1 : { writes(No) # No出力 writes(fs,s) } # データ出力 2 : writes(fs,s) 0 : { writes(fs,s) write() } # 改行出力 default : stop("case error") # それ以外だったら } } if (n % N) ~= 0 then write() # 最後の出力行の要素数が、N以外だったら } # 改行出力 # 動作参考用 write("\nT1 (table) 構\造") # "構" は "\x8d5c" なので、"\x5c"を補う。 every write(x2t(T1)) # x2tは 構造体の内容を tree表示する procedure # x2tree06.icnに含まれる。 write("\nL1 (list) 構\造") every write(x2t(L1)) end # 解説 # /T1[n] : T1[n]の null (未定義) check # table : table(AWKの連想配列)を生成 ()内には、初期値を指定できる # every ... !L : 構造体 Lの要素を順次取り出して -----$ SHAPE02.ICN ( lines:61 words:277 ) ------------------ + <- integer18 | + string "遊佐 未森" + <- integer10 | + string "竹内 まりあ" | + string "中島 みゆき" | + string "中山 美穂" | + string "中村 あゆみ" | + string "久松 史奈" | + string "松任谷 由美" | + string "森川 美穂" | + string "山下 久美子" + <- integer7 | + string "篠原 ともえ" | + string "篠原 美也子" | + string "篠原 涼子" + <- integer1 | + string "相川 七瀬" | + string "安室 奈美恵" + <- integer3 + string "大黒 摩季" + string "岡本 真夜" + string "尾崎 亜美" + string "辛島 美登里" L1 (list) 構造 + | + integer1 | + | + string "相川 七瀬" | + string "安室 奈美恵" + | + integer3 | + | + string "大黒 摩季" | + string "岡本 真夜" | + string "尾崎 亜美" | + string "辛島 美登里" + | + integer7 | + | + string "篠原 ともえ" | + string "篠原 美也子" | + string "篠原 涼子" + | + integer10 | + | + string "竹内 まりあ" | + string "中島 みゆき" | + string "中山 美穂" | + string "中村 あゆみ" | + string "久松 史奈" | + string "松任谷 由美" | + string "森川 美穂" | + string "山下 久美子" + + integer18 + + string "遊佐 未森" -----$ SHAPE02 ( lines:71 words:285 ) ---------------------- BGM: Vivien/篠原美也子 (nif0516b.txt 1997/05/16 niftyserve fgalts, 1997/05/17 PCVAN SLABO) ■ Icon > ちょっと Icon(4)data整形 風つかい 相変わらず、1行データを 3個づつまとめる データ整形 の お話しの 続きです。 前回の programの出力部分に generatorを使ってみます。 generatorと いうのは、procedureで、複数の結果を、順次返す機能 を、持っている ものを言います。 でも、全体は、同じ 機能の programですから、 動いた結果も、相変わ らず 同じです。(笑) -----^ SHAPE03.ICN ( date:97-05-17 time:10:46 ) ------------kekka # data sample(/line) : "1","A" # This file is in the public domain. link retrieve procedure main(args) Usage := "shape03 data_file >kekka" if *args = 0 then stop(Usage) # command line 引数 check dir := open(args[1]) | stop("cannot open ",args[1]) # file open # 初期設定 fs := '\,' # 行内の data区切り N := 3 # 区切り数 T1 := table() # 空の tableを生成 # data fileを読み込み tableに格納 (tableは AWKの連想配列と同じ) while line := read(dir) do { # 1行づつ順次読み込んで、 line ? { # 読み込んだ 行を 走査対象として ps := upto(fs) # 最初の fsの位置を得る n := numeric(line[1:ps]) # fsより前が n (numeric :文字→数字変換) s := line[ps+1:0] # fsより後が s /T1[n] := [] # T1に nが keyとして存在しないなら # T1に nを keyとして、空 listを登録 put(T1[n],s) # tableの key n の value (list)に sを追加 } } L1 := sort(T1,1) # T1を keyで sort(結果は listの list) write("整形結果") every L2 := !L1 do { # L1の要素を 順次取り出して No := L2[1] # data種別 No every L3 := retrieve(L2[2],3) do { # L2[2]の要素を 3個づつ取り出す。 writes(No) # data種別 No を出力 every writes(fs,!L3) # L3の要素を 順次出力 write() } } end -----$ SHAPE03.ICN ( lines:43 words:194 ) ------------------ BGM: Vivien/篠原美也子 (nif0516c.txt 1997/05/16 niftyserve fgalts, 1997/05/17 PCVAN SLABO) ■ Icon > Icon回り道(1)重複しないランダムな 10個の数 風つかい (PCVANの SLABOで、乱数の話題がでていました。Iconで乱数をつかったこと がありませんでしたので、話題に相乗りして Iconでもやってみました。 テーマは、3桁の重複しない乱数を得る、というものです。) ということで、私も Iconの乱数機能を 使ったことが、ありませんでした ので、どういう programになるか やってみました。。 Iconでは、?10 と書きますと、1〜10迄の乱数を発生できます。 正確には 乱数列の最初の数字を得ることができます。 同じ program内で、次にこの式 (?10) を 実行すると、乱数列の2番目の 数字が得られます。 ちなみ、文字列では、?"ABCDEFG" と書くと "ABCDEFG"から randomに1文 字(1-byte character)取り出せます。 Iconでは、この乱数列は programを何度実行しても同じ乱数列となります。 違う乱数列を得るためには、keywordの &randomを変えると可能だそうです。 1 〜 n までの randomな数 を m個 発生させる programは次のようになり ます。 -----^ RANDOM01.ICN ( date:97-05-30 time:23:22 ) ----------- BGM: 河よりも長くゆるやかに/篠原美也子 (pcv0531a.txt 1997/05/31 PCVAN SLABO) ■ Icon > Icon回り道(2)重複しないランダムな 10個の数 風つかい 続きです。 乱数列 program実行の度に、同じ乱数列が出てくるのでは、まずい ときには、乱数の種を 乱数にする procedureが BIPL(Icon基本 library)に 有り ますので、それを linkして、乱数列発生の前に実行します。 -----^ RANDOM04.ICN ( date:97-05-31 time:14:41 ) -----------> random04 を3回実行した時の結果です。 -----^ RANDOM04 ( date:97-05-31 time:14:42 ) --------------- BGM: 河よりも長くゆるやかに/篠原美也子 (pcv0531b.txt 1997/05/31 PCVAN SLABO) ■ Icon > Icon回り道(3)重複しないランダムな 10個の数 風つかい 3桁の数字という条件で、そのものズバリ 文字数が3桁 というやり方を書き落と しました。そういう条件の場合は、次のようになります。 -----^ RANDOM07.ICN ( date:97-06-01 time:23:49 ) ----------- BGM: そのままの君でいて/岡本真夜 (pcv0601a.txt 1997/06/01 PCVAN SLABO) ■ Icon > Icon回り道(4)Usageが... 風つかい あれれ、random02.icnから random07.icnまで、全部 Usageのファイル名が random01のままですね。あはは。よくやります。 人手で修正するので、漏れがでますので、プログラム自身で作らせようと こんな procedureを作ってみました。 Iconには、プログラム名称を示す keywordがあります。 &prognameです。 &prognameには、ディレクトリー付きのファイル名が入ります。 Usageでは、ディレクトリーと拡張子は、通常 不要ですので、それを削除 して使います。 top_cut()という procedureで、ディレクトリーを削除して、 top_get()という procedureで、拡張子を除いています。 (参考に &prognameそのものも表示するようにしています。) -----^ EXE_NAME.ICN ( date:97-06-02 time:19:06 ) -----------