UWSC|カンマ等で区切られたフィールドデータを扱う自作関数

ポク太郎です。

プログラム中、変数定義が面倒な際にフィールドデータを作ります。フィールドデータとは以下のようなもの。
 フィールドデータ…カンマ等で区切ったデータ:tarou,hanako,jirou,pokutaro

このように整理しておくと変数を減らせます。昔触った環境に便利な関数があったのでそれを移植します。


関数の仕様は

以下の関数を作成しようとしています。

 フィールドデータ:txt=tarou,hanako,jirou,pokutaro
があったときに、
 N=nthc(txt,”,”)とすると、4を返す関数(フィールドの数)
 T=nthf(txt,”,”,3)とすると、“jirou”を返す関数(フィールドの3番目)

あまりに巨大なフィールドを渡すとスピードが出ませんが、UWSCで鬼計算はしないと思いますので。

C言語のstrstr関数を移植

準備としてC言語のstring.hからstrstr関数を移植。これから作る自作関数中から呼び出して使用します。

C言語strstr関数仕様:引数txt中から引数schを検索し、schより後ろの文字列を返す関数。

FUNCTION strstr(txt,sch)
//txt中にschが無い、txtよりschが長い場合""空白が返される
            r=""
   FOR i=1 TO LENGTH(txt)
            search=COPY(txt,i,LENGTH(sch))
      IF (search=sch)
            r=COPY(txt,i)
            BREAK
      ENDIF
   NEXT
            RESULT=r
FEND
関数プログラム説明(○⇒:成り立つ場合、×⇒:成り立たない場合)
1,12行目 1
12
FUNCTION strstr(txt,sch)
FEND
strstr(txt,sch)関数宣言
引数txt中からschを検索し、schより後ろの文字列を返す関数。(C言語仕様)
3行目 3             r=
戻り値として使用する変数rの初期化。
4,10行目 4
10
   FOR i=1 TO LENGTH(txt)
   NEXT
引数txtを1番目から(文字数)番目までスキャンするためのループ。
5行目 5             search=COPY(txt,i,LENGTH(sch))
変数searchi番目から(引数schの文字数)個を取り出して代入。
COPY(DT,N1,N2)DTN1番目の文字からN2個の文字列を返すUWSC標準関数。
6~9行目 6
7
8
9
      IF (search=sch)
            r=COPY(txt,i)
            BREAK
      ENDIF
もし5行目で代入したsearchと引数schが等しいなら、
変数rに引数txti番目から引数txtの最後までを代入。(UWSCのCOPY関数は第3引数を省略すると最後までとなる)
BREAKはループを出ろというUWSCの命令。
11行目 11             RESULT=r
関数の戻り値として変数rを返す。

フィールドの数を返すnthc関数

ループ命令を使ってフィールドの中身をスキャンするので、フィールドの数をすぐに得られるようにします。その数を返すのがnthc関数。

FUNCTION nthc(txt,spr)
            n=1
            s=txt
   IF strstr(s,spr)=""
            n=0
   ELSE
      WHILE (0=0)
            s=strstr(s,spr)
            s=COPY(s,LENGTH(spr)+1)
            n=n+1
      IF strstr(s,spr)=""
         BREAK
      ENDIF
      WEND
   ENDIF
            RESULT=n
FEND
関数プログラム説明(○⇒:成り立つ場合、×⇒:成り立たない場合)
1,17行目 1
17
FUNCTION nthc(txt,spr)
FEND
nthc(txt,spr)関数宣言:
引数txt(フィールドデータ)に引数spr(セパレータ)で区切られたフィールドがいくつあるかを返す関数。
2,3行目 2
3
            n=1
            s=txt
使用する変数の初期化。
変数nはフィールドの数を数えるのに使用。
変数sはフィールドデータを調べるための文字列として使用。
4,6,15行目 4
6
15
   IF strstr(s,spr)=
   ELSE
   ENDIF
条件分岐。フィールドデータにセパレータが含まれてないかどうかを調べる。
5行目 5             n=0
○⇒(フィールドデータにセパレータが含まれてない)
変数nに0を代入し、16行目へ。
7,11~14行目 7
11
12
13
14
      WHILE (0=0)
      IF strstr(s,spr)=
         BREAK
      ENDIF
      WEND
×⇒(フィールドデータにセパレータが含まれている)
 8~10行目のループへと突入。
 ループ条件を0=0としてあるので、常に成り立ち無限ループとなるが、WHILE (1)と書く人が多いかも。) BREAK条件を、strstr関数を実行した結果変数sが空白となったとき、として制御する。(8行目を通るたびに変数sは削られていくことに注目。)
8~10行目 8
9
10
            s=strstr(s,spr)
            s=COPY(s,LENGTH(spr)+1)
            n=n+1
この部分を通るたびに1つ後のセパレータの次のデータを先頭に持ってくるためのルーチン。
 変数sにstrstr関数の戻り値を代入。
 8行目を通るたびに変数sはセパレータを先頭とする文字列に置き換わっていく。
 9行目で変数sの先頭にあるセパレータ部分を取り除く。(UWSCのCOPY関数は第3引数を省略すると最後までとなる)
 10行目の変数nはフィールドの数をなのでここでインクリメント。
16行目 16             RESULT=n
関数の戻り値として変数nを返す。

指定したフィールドのデータを返すnthf関数

実際にフィールドデータを取り出すnthf関数。引数として区切りの文字とn番目を渡します。

FUNCTION nthf(txt,spr,n)
            i=0
            tmp=""
            nlast=nthc(txt,spr)
            r=""
   IF (n<1)
   ELSEIF (txt="")
   ELSEIF (strstr(txt,spr)="")
   ELSEIF (n>nlast)
   ELSE
            s=txt
      WHILE (0=0)
            tmp=s
            s=strstr(s,spr)
            i=i+1
         IF (i=n)
            n1=LENGTH(tmp)
            n2=LENGTH(s)
            r=COPY(tmp,1,n1-n2)
            BREAK
         ELSE
            s=COPY(s,LENGTH(spr)+1)
         ENDIF
      IF s=""
         BREAK
      ENDIF
      WEND
   ENDIF
            RESULT=r
FEND
関数プログラム説明(○⇒:成り立つ場合、×⇒:成り立たない場合)
1,30行目 1
30
FUNCTION nthf(txt,spr,n)
FEND
nthf(txt,spr,n)関数宣言:
引数spr(セパレータ)で区切られた引数txt(フィールドデータ)から引数n番目のデータを返す関数。
2~5行目 2
3
4
5
            i=0
            tmp=
            nlast=nthc(txt,spr)
            r=
使用する変数の初期化。
変数iはフィールドの数を数えるのに使用。
変数tmpは変数sを退避するため一時的に使用。
変数nlastはフィールドデータの数を代入。
変数rは関数の戻り値を記憶しておくために使用。初期値として空白を入れておく。
6~10,28行目 6
7
8
9
10
28
   IF (n<1)
   ELSEIF (txt=)
   ELSEIF (strstr(txt,spr)=)
   ELSEIF (n>nlast)
   ELSE
   ENDIF
条件分岐いろいろ:面倒なので最初に弾ける条件は弾いてしまいます。
5行目で変数rに空白を代入してあるので弾かれた条件ではそのまま29行目で空白を関数の引数として返します。
 6行目: 0番目が指定された ⇒戻り値に空白返す。
 7行目: フィールドデータとして空白が渡された ⇒戻り値に空白返す。
 8行目: フィールドデータ中にセパレータが含まれてなかった ⇒戻り値に空白返す。
 9行目: フィールドの数より大きなnを指定された ⇒戻り値に空白返す。
×⇒(上記すべて成り立たなかった)
 11~27行目を実行。
11行目 11             s=txt
使用する変数の初期化。
変数sはフィールドデータを調べるための文字列として使用。初期値は引数txt
12,24~27行目 12
24
25
26
27
      WHILE (0=0)
      IF s=
         BREAK
      ENDIF
      WEND
nthc関数の7,11~14行目と同じ条件のループ。(14行目を通るたびに変数sは削られていくことに注目。)
13~15行目 13
14
15
            tmp=s
            s=strstr(s,spr)
            i=i+1
 13行目: 変数tmpに変数sを一時的に退避。
 14行目: 変数sstrstr関数の戻り値を代入。
       通るたびに変数sはセパレータを先頭とする文字列に置き換わっていく。
 15行目: 変数iはフィールドの数をなのでここでインクリメント。
16~23行目 16
17
18
19
20
21
22
23
         IF (i=n)
            n1=LENGTH(tmp)
            n2=LENGTH(s)
            r=COPY(tmp,1,n1n2)
            BREAK
         ELSE
            s=COPY(s,LENGTH(spr)+1)
         ENDIF
条件:もしフィールドの数を数えている変数iと引数として指定された引数nが同じなら、(目的のデータが見つかったなら)
○⇒
 17行目: 退避していた変数tmpの文字数を変数n1に代入。
 18行目: 現在の変数sの文字数を変数n2に代入。
 19行目: 変数rに目的のフィールドの文字列を代入。
      (退避していた変数tmpの1文字目から(n1-n2)個がそれに相当します。)
 20行目:見つかったのでループを抜ける。
×⇒
 22行目: 変数sの(セパレータの文字数+1)番目からの最後までを変数sに代入。
      (先頭にあったセパレータを取り除いたものを変数sとして再度13行目のループ先頭へ戻る。)
      (UWSCのCOPY関数は第3引数を省略すると最後までとなる) 
29行目 29             RESULT=r
関数の戻り値として変数rを返す。

フィールドデータを扱う関数動作確認

動作確認プログラムがこちら。

   PRINT "nthc kotae ="+nthc("abc[]defghi[]jklm[]nop","[]")
   PRINT "nthc kotae ="+nthc("[]defghi[]jklm[]nop","[]")
   PRINT "nthc kotae ="+nthc("abc[]defghi[]jklm[]","[]")
   PRINT "---------------------------"
FOR i=-1 TO 5
   PRINT "nthf kotae "+i+"="+nthf("abc[]defghi[]jklm[]nop","[]",i)
NEXT
   PRINT "---------------------------"
FOR i=-1 TO 5
   PRINT "nthf kotae "+i+"="+nthf("[]defghi[]jklm[]","[]",i)
NEXT
プログラム説明(○⇒:成り立つ場合、×⇒:成り立たない場合)
1~3行目 1
2
3
   PRINT nthc kotae =+nthc(abc[]defghi[]jklm[]nop,[])
   PRINT nthc kotae =+nthc([]defghi[]jklm[]nop,[])
   PRINT nthc kotae =+nthc(abc[]defghi[]jklm[],[])
nthc関数の動作確認です。
セパレータとして”[](2文字)を渡したときに、先頭と終端がセパレータではない、先頭がセパレータでである、終端がセパレータであるフィールドデータを渡して正常にカウントするか確認します。
5~7行目 5
6
7
FOR i=1 TO 5
   PRINT nthf kotae +i+=+nthf(abc[]defghi[]jklm[]nop,[],i)
NEXT
nthf関数の動作確認です。
先頭・終端がセパレータでないフィールドデータを与えたときの挙動を確認します。(n番目の指定は-1~5までやってみます。)
9~11行目 9
10
11
FOR i=1 TO 5
   PRINT nthf kotae +i+=+nthf([]defghi[]jklm[],[],i)
NEXT
nthf関数の動作確認(変則なフィールドデータ)です。
先頭・終端がセパレータであるフィールドデータを与えたときの挙動を確認します。(n番目の指定は-1~5までやってみます。)
4,8行目 4
8
   PRINT
   PRINT
結果を見やすくするための区切りを表示。


結果は、

nthc kotae =4
nthc kotae =4
nthc kotae =4
---------------------------
nthf kotae -1=
nthf kotae 0=
nthf kotae 1=abc
nthf kotae 2=defghi
nthf kotae 3=jklm
nthf kotae 4=nop
nthf kotae 5=
---------------------------
nthf kotae -1=
nthf kotae 0=
nthf kotae 1=
nthf kotae 2=defghi
nthf kotae 3=jklm
nthf kotae 4=
nthf kotae 5=

一応思い通りの仕様の動きができました。

文字列関連の話題
文字列操作あれこれMID、LEFT、RIGHT、LEN 文字列中に指定ワードあるなし判定関数 現在のタイムスタンプ文字列を返す関数 カンマ等で区切られたフィールドデータを扱う関数
本記事の内容は以下でした。
○フィールドデータの数を返す関数

戻値(int)=nthc(txt,spr)

○指定されたフィールドデータを返す関数

戻値(str)=nthf(txt,spr,n)

コメント

タイトルとURLをコピーしました