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

UWSCという開発ソフトを使って作り上げる作業を実際にやってみるカテゴリ。準備した自作関数や使い方、トラブルを記事にしていきます。得るものがあれば幸いです。
今回は、プログラム中でちょこっと記憶したりするのに便利なフィールドデータを扱う関数です。

記事の内容
○フィールドデータの数を返す関数

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

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

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

以前公開した関数なのですが、セパレータとして1文字しか使用できないという制限があったので修正します。

プログラム中でちょこっとデータを記憶しておきたい場合などフィールドデータを作りたいときがあります。フィールドデータとは以下のようなもの。

フィールドデータ(カンマ区切りのデータなど)tarou,hanako,jirou,pokutaro

このようにデータを整理しておくと扱わないといけない変数を減らせるので便利です。昔使った環境に便利な関数があったのでそれを移植します。(この関数は論理が複雑…というかちょっとややこしいです。)

関数の仕様は

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

このような関数を作成しようとしています。(あまりに大きなフィールドを渡すとスピードが出ない気もしますが、UWSCですので便利な方がいいかなと思います。)

C言語のstrstr関数を移植

準備としてC言語のstring.hから一つ関数を移植(擬似移植?)。nthc、nthf関数の中からこの関数を呼び出して使用します。(既にCへ移植してあったのでそこからUWSCに引っ張ってきたのでこうしただけ。特に意味はありません。方法は他にもあります。)

strstr関数(C言語仕様)
1
2
3
4
5
6
7
8
9
10
11
12
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関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
関数の戻り値として変数rを返す。


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

nthf関数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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,n1n2)
            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を返す。


動作確認

動作確認プログラム
1
2
3
4
5
6
7
8
9
10
11
   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=

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

記事の内容は伝わりましたでしょうか。
○フィールドデータの数を返す関数

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

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

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

コメント

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