WWW とかで使われている, 文字列のエンコード/デコード処理[1]。 これらは 〆(^^) の作った SilkWeb の部品 (←最近宣伝が多い気がする (^^;)
HexDcoStr: procedure res = '' txt = translate(arg(1), '00'x' ', '&+') do while length(txt) > 0 parse var txt pre '%'+1 hx +2 txt if datatype(hx, 'x') then res = res||pre''x2c(hx) else res = res||pre'%'hx end return res
(↑)デコードには少し問題があって %00 と & を同じとみなしている。
普通は, 害はないはず (^^; (←普通は って…)
引数に変換された文字列を指定すると, デコードしたデータを返す。
/* 以下の文字を取り除く */ /* reserved = ';/?:@&=+', unsafe = xrange(, '1f'x)'7f'x' "#%<>' */ HexEncStr: procedure parse arg val, str unsafe = ';/?:@&=+'xrange('80'x, '1f'x)'7f'x'"#%<>'str res = '' do forever p = verify(val, unsafe, 'M') if p == 0 then leave parse var val pre =(p) ch +1 val res = res''copies(pre, p > 1)'%'c2x(ch) end return translate(res''val, '+', ' ')
(↑)エンコードは, 制御文字や 0x80以上の文字を %xx に変換する。 対象になる そういった文字は環境(?)でも異なるので, 変換したい文字群を引数で指定する事ができる。
使い方: txt = HexEncStr(data)
とか, txt = HexEncStr(data, '*-')
とか。
◇
ど〜も, SilkWebを切り売りしているみたい (^^; (←いや, 売ってるわけじゃないんだけれどもね) この調子でいったら, ここに載せているのをつなげていったら復元できるかも (^^
application/x-www-form-urlencoded
かな?Classic Rexx では, 拡張属性(EA)の読み書きはできるけど (←あ, もちろん OS/2ね), ではどういう種類のものがあるのか, そのリストを得ることは(標準では)できない。 〆(^^) のソフトの UrlEncや Dr.EAなどでは次の関数を使っている。
EAnames: parse arg ,EAnames; return EAnamesCore(arg(1)); EAnamesCore: procedure expose (EAnames) parse arg src parse var EAnames EAname EAval . fname = SysTempFileName('Temp???.EA') '@EAutil /s /p' /* split permanent */ '"'src'"' fname if stream(fname, 'C', 'Query Exists') = '' then return 0 ea = charin(fname,, chars(fname)) Rc = stream(fname, 'C', 'Close') Rc = SysFileDelete(fname) if Rc \= 0 then say 'SysFileDelete='Rc ln = ii2d(left(ea, 4)) /*if length(ea) \= ln then say 'EAerr' length(ea) d2x(ln)*/ ea = left(ea, ln) p = 4 +1 do cnt = 1 while p < length(ea) parse var ea =(p)/* '0'x */+1 ln +1 n +2 str '0'x call value EAname''cnt, str ln = c2d(ln); n = ii2d(n); if EAval > '' then call value EAval''cnt, substr(ea, p +5 +ln, n) p = p +5 +ln +n end return cnt -1
まず, その使い方。
cnt = EAnames('\Desktop', 'eal.')
のように指定すると (←'Desktop' の部分はその環境に合わせて読み替えて下さい) cnt
に EAの件数, eal.1 〜 eal.cnt
にそれぞれ名前が入る。
順番は登録されている順番なので特に意味なし。で, あとは EAを読み書きするだけ。
また, cnt = EAnames('\Desktop', 'eal. eav.')
のように指定すると eav.1 〜 eav.cnt
には EAの値 (と表現してよいのか (^^; ) が入る。
つまり, いちいち SySGetEA
を呼びださなくてもよい。
二重呼び出しによって変数を保護しているため, 問題点としては 関数内で使っている変数と 同じ名前を引数に指定すると何らかの異常な動作をしてしまう。
ほかにも問題があるかもしれない。というのは, この EAの構造は(勝手な)予想なので, 間違いがあるかもしれない (^^;; ということ。がっはっは。
げっ, いま気付いたけど 必要なソフトに eautil.exe があるじゃないか (^^;; あ〜どうしよう, 必須のソフトの項目に記入していなかったような。 たぶん OS/2 標準だからよいかも (↑黙ってよう (^^;)
Rexx には数値変数とかはない。なにせ型がない。 だから数値を扱う時にかなり便利。 (その上)数値を入れる器の大きさを考えなくてよい。肩の荷がおりたって感じ (^^
唯一あるのは 標準で 9桁という制限。しかしこれも numeric digit 50
とか指定すれば OK。
ただし min(), max()
などの内部関数は 9桁としてしか処理されないことが多い (自分で これらの関数を作れば OK)。
で, 文字列も数値も同じ扱いなので, 次のようにできる。
say right(n, 5) /* 5桁で表示 */ say right(n, 5, 0) /* 前ゼロ */
変数に入っている値が数字かどうかを調べるには, datatype(n, 'N')
とかで調べる事ができる。
もし整数(自然数だっけ?)であるかどうかを調べるのなら, datatype(n, 'W')
を使う。
◇
ファイルにデータを保存する時とか, Network にデータを流す時とか, こういう場合, Byte順序が問題なる。 Rexx 単体でなら, なにも悩む事はない。けど他のプログラムが作成したデータを 読む時などに気にする必要が出てくる。
Rexxでは, 数値を d2c()で変換すると big-endian形式になる。 Net形式というか, モトローラ形式というか ・・・
OS/2 は現在インテルの石の上で動いているので, つまり逆の形式。このため変換する必要がある。〆(^^) がよく使うのは, こんなの。
d2li: d2ii: return reverse(d2c(arg(1), word(arg(2) 2, 1))); li2d: ii2d: return c2d(reverse(arg(1)), word(arg(2) 4, 1));
d2li(), d2ii()
は数字から little-endian形式に変換, li2d(), ii2d()
は little-endian形式から数字に変換する。
なぜ, それぞれ 2つ(の関数名が)あるかというと, 作ってしばらく使った後, 名前を変えたため。あはは。
iiが付いている方が古く, li が付いている方が新しい (って, 関係ないけど)。
ちなみに iiは intel integerで liは little-endian integerの略のつもり。
使い方は, 次の通り。
<Byte列> = d2li(<数字> [, <Byte数>]) /* Byte数 は省略可能 */ <数字> = li2d(<Byte列> [, <Byte数>]) /* Byte数 は省略可能 */
ある時突然 sortが必要になったので適当に作ったものです。(←何かは秘密)
後で調べてみると, どうも quick sortっぽい。
本に紹介されていた quick sort は C言語なもので, 微妙に違う部分もあった。
けど高速化の関係だろう(と思う) ・・・ ということで, 公開。
QuSort: procedure /* Quick Sort? */ parse arg lst le = ''; gr = ''; p = random(1, words(lst)) val = word(lst, p) do i = 1 to words(lst) if i == p then iterate v = word(lst, i) if v < val then le = le v else gr = gr v end if words(le) > 1 then le = QuSort(le) if words(gr) > 1 then gr = QuSort(gr) return space(le val gr)
んで, 使い方は data = 'this is a pen'
とかやって, res = QuSort(data)
とかする。
そうすると並んだ結果が返される。
途中にピリオドの付いた変数は, 複合記号(=Compound Symbols) と呼ばれる。
その中で, 先頭から最初のピリオドまでの部分を 語幹(=stem) といい, それ以降の部分を 語尾(=tail) という。
array.i.jarray.... 語幹(=stem)i.j... 語尾(=tail) |
配列みたいな使い方ができるが, しかし tail は数値でなくてもよい (AWK などスクリプト言語で使われる連想配列みたいなもの)。 このため, 次のようなこともできたりする。
n = '00'x table.n = 'binary'
でも, (↓)このようにはできない。こうしてしまうと文字列の連結になってしまうから。
say table.'00'x
※ tail の部分は Object REXX や NetRexx では []
で囲むことで指定でき, さらに使いやすくなっている。つーか, ソレが普通かも。
また, 初期設定されていない変数は 大文字に変換された記号自体の文字が値になる, という Rexxの特色をいかして, 次のように使う事ができる。
item.n._TITLE = n'番目の項目のタイトル' item.n._PAGE = nPage /* n番目の項目のページ数 */
この場合, '_TITLE' などの部分は, 変数名と重なったらアウトなので, (↑)のように 先頭に '_' を付けるとよいかも。
でも, そういう名前にしておきながら, そういう変数を使っては何にもならない。 ( _TITLE = 'xxx'
とか)
ほかにも, こういう名前もよいかも。
TITLE! TITLE? !TITLE ?TITLE __TITLE
◇
tail が文字でもよいというのは, 別の意味でも便利だったりする。 次にあげるのは, それぞれ別々の変数となる。(特に, 0 と 00 とかは違うことに注意)
table.0 table.00 table..0 table.0.
また, 【図1】での場合, 実際には次のものと同じ。(【図1】では n は使われていないが)
別の言語でいうところの array['1.2'] とか, そんな感じ。
つまり二次元配列でもなんでもない。ってこと。
n = i'.'j array.n
で, これらの変数が使われているかどうかを調べる方法。
if symbol('table.i') == 'LIT' /* ←望ましい */ if symbol('table.'i) == 'LIT'
しかし table. = ''
を実行していると, (値が既に割り当てられている訳なので)この条件判断は利用できない。