一歩進んだ Rexx

Classic REXX (REstructured eXtended eXecutor)
File #02

URLエンコード/デコード

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を切り売りしているみたい (^^; (←いや, 売ってるわけじゃないんだけれどもね) この調子でいったら, ここに載せているのをつなげていったら復元できるかも (^^


[1]
何て言う名前のエンコードなのか分からない (T_T
ここではとりあえず 16進エンコードとか名前を付けている。 本当は application/x-www-form-urlencodedかな?

拡張属性(EA)一覧

Classic Rexx では, 拡張属性(EA)の読み書きはできるけど (←あ, もちろん OS/2ね), ではどういう種類のものがあるのか, そのリストを得ることは(標準では)できない。 〆(^^) のソフトの UrlEncDr.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での数値の扱い

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数 は省略可能 */

QuSort (Quick Sort?)

ある時突然 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) というもの

基礎

途中にピリオドの付いた変数は, 複合記号(=Compound Symbols) と呼ばれる。
その中で, 先頭から最初のピリオドまでの部分を 語幹(=stem) といい, それ以降の部分を 語尾(=tail) という。

【図1】
array.i.j

   array.  ... 語幹(=stem)
   i.j     ... 語尾(=tail)

配列みたいな使い方ができるが, しかし tail は数値でなくてもよい (AWK などスクリプト言語で使われる連想配列みたいなもの)。 このため, 次のようなこともできたりする。

  n = '00'x
  table.n = 'binary'

でも, (↓)このようにはできない。こうしてしまうと文字列の連結になってしまうから。

  say table.'00'x

※ tail の部分は Object REXX や NetRexx では [] で囲むことで指定でき, さらに使いやすくなっている。つーか, ソレが普通かも。

Tail

また, 初期設定されていない変数は 大文字に変換された記号自体の文字が値になる, という 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. = '' を実行していると, (値が既に割り当てられている訳なので)この条件判断は利用できない。


Copyright (C) 1998-2004 Rexx使いの織華
email: ori@drive.co.jp