IBM i (旧AS400）の自己学習メモ　RPG Ⅳプログラミング編０４　正規表現による電話番号チェック処理（RegExp_Count）

# 自己学習メモインデックス

[IBM i (旧AS400）の自己学習メモ　インデックス](https://qiita.com/You_name_is_YU/items/3964849e7803c8eaf767)

# 概要

得意先マスタメンテナンス画面のコーディングを進めており、チェック処理の実装をする中で電話番号のチェック処理に着手しました。
そこで、Javaや.Net開発ではよくやっていた、電話番号の正規表現チェックを行おうと思って色々調べていたのですが、あんまりそういった情報が見当たりませんでした。
色々と試行錯誤して、**定位置記入方式**での実現にこだわりすぎないように一部妥協して、**SQLRPGLE**のコードの一部でフリーフォーマットのRPGプログラミングをしてしまいましたが、そのチェック処理を呼び出す元となるRPGは**定位置記入方式**のコーディングで実現できましたので、記録としてQiita記事に残します。

# サンプルソース

## チェック処理用のSQLRPGLE

以下は、電話番号の正規表現チェック処理を行うSQLRPGLEプログラムです。
第１引数に指定された電話番号をチェックし、その結果を第２引数のP@RTCD（リターンコード）にセットして処理を終了します。
電話番号はハイフンを含んだ形式のみを許可し、それ以外は全てエラーになるようにしています。

```shell:CHECK_TEL(*SQLRPGLE)
 FMT H  HKeywords++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        *************** Beginning of data *****************************************
0001.00 H                                                                          
0002.00  * 正規表現用の変数                                                      
0003.00 D Regex           S             50A   Inz Varying                          
0004.00  * マッチした件数                                                        
0005.00 D Cnt             S              1S 0                                      
0006.00  * パラメータ：電話番号                                                  
0007.00 D P@TEL           S             15A                                        
0008.00  * パラメータ：リターンコード                                            
0009.00 D P@RTCD          S              2A                                        
0010.00  * パラメータリスト                                                      
0011.00 C     *ENTRY        PLIST                                                  
0012.00 C                   PARM                    P@TEL                          
0013.00 C                   PARM                    P@RTCD                         
0014.00  /Free                                                                     
0015.00  //Regex = '^0¥d{1,3}-¥d{1,4}-¥d{4}$';                                     
0016.00    Regex = '^0[0-9]{1,3}-[0-9]{1,4}-[0-9]{4}$';                            
0017.00    Exec Sql Set Option Commit = *None;                                     
0018.00    Exec Sql                                                                
0019.00      Set :Cnt = RegExp_Count(Trim(:P@TEL), :Regex);                        
0020.00      If Cnt = 1;                                                             
0021.00        P@RTCD = 'OK';                                                        
0022.00      Else;                                                                   
0023.00        P@RTCD = 'NG';                                                        
0024.00      EndIf;                                                                  
0025.00     *INLR = *ON;                                                             
0026.00  /End-Free                                                                   
        ****************** End of data **********************************************
```

`Inz Varying`と指定しているのは、Regex変数に指定する正規表現文字列に余分な空白文字列が入ってしまい、正規表現として正しい値にならず、正規表現のチェック処理が正常に動作しなくなってしまうので、固定長文字列ではなく可変長文字列の定義とするために指定しています。

```shell
0002.00  * 正規表現用の変数                                                      
0003.00 D Regex           S             50A   Inz Varying  
```

- `0016.00    Regex = '^0[0-9]{1,3}-[0-9]{1,4}-[0-9]{4}$';`
    - 電話番号の正規表現です。
    - 本当は0015.00にコメントアウトしている定義を利用したかったのですが、どうやら`\`をPUB400.comの環境では正しく認識してくれないようで、どうやっても正規表現のチェックが正常に動作しませんでした。
    - 検証方法としては、STRSQLで`values regexp_count('03-3333-3333', '^0[0-9]{1,3}-[0-9]{1,4}-[0-9]{4}$')`こんな感じで実行すると動作確認が簡単にできます。
- `0019.00      Set :Cnt = RegExp_Count(Trim(:P@TEL), :Regex);`
    - `P@TEL`は、固定長文字列のためTrim関数をかまして余分な空白を取り除いています。
    - `P@TEL`自体も`Inz Varying`と指定して可変長文字列にすればいいのかなぁと思って指定してみたのですが、パラメータで指定する変数にはこの指定が使えないと怒られてしまいました。

```shell
0014.00  /Free                                                                     
0015.00  //Regex = '^0\d{1,3}-\d{1,4}-\d{4}$';                                     
0016.00    Regex = '^0[0-9]{1,3}-[0-9]{1,4}-[0-9]{4}$';                            
0017.00    Exec Sql Set Option Commit = *None;                                     
0018.00    Exec Sql                                                                
0019.00      Set :Cnt = RegExp_Count(Trim(:P@TEL), :Regex);                        
0020.00      If Cnt = 1;                                                             
0021.00        P@RTCD = 'OK';                                                        
0022.00      Else;                                                                   
0023.00        P@RTCD = 'NG';                                                        
0024.00      EndIf;                                                                  
0025.00     *INLR = *ON;                                                             
0026.00  /End-Free 
```

## 電話番号チェック処理を呼び出すRPGLE

上記で定義したCHECK_TELを呼び出すRPGプログラムです。
UnitTestみたいなイメージで、テストデータ（電話番号データ）を配列で定義して、電話番号の正規表現チェック処理が正常に動作しているのかテストするような感じで作ってみました。

```shell:RPG92(*RPGLE)
 FMT *   *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
        *************** Beginning of data *****************************************
0001.00  * テスト用の電話番号配列定義                                            
0002.00 D @@DSTELS        DS                                                       
0003.00 D                               15    INZ('03-1234-5678')                  
0004.00 D                               15    INZ('090-1234-5678')                 
0005.00 D                               15    INZ('0120-1234-5678')                
0006.00 D                               15    INZ('0312345678')                    
0007.00 D                               15    INZ('09012345678')                   
0008.00 D                               15    INZ('012012345678')                  
0009.00 D @@TELS                        15    DIM(6) OVERLAY(@@DSTELS)             
0010.00  * インデックス変数                                                      
0011.00 D i               S              1P 0 INZ(1)                               
0012.00  * デバッグ表示用の文字列                                                
0013.00 D str             S             20A                                        
0014.00  * パラメータリストの定義                                                
0015.00 C     PL@TEL        PLIST                                                  
0016.00 C                   PARM                    P@TOKTEL         15            
0017.00 C                   PARM                    P@RTCD            2            
0018.00                                                                            
0019.00  * 電話番号の正規表現チェック処理実行                                    
0020.00 C                   FOR       i = 1 to 6 by 1                              
0021.00 C                   EVAL      P@TOKTEL = @@TELS(i)                         
0022.00 C                   EVAL      P@RTCD = ''                                  
0023.00 C                   CALL      'CHECK_TEL'   PL@TEL                         
0024.00 C     @@TELS(i)     CAT       P@RTCD        str                            
0025.00 C     str           DSPLY                                                  
0026.00 C                   ENDFOR                                                 
0027.00                                                                            
0028.00 C                   EVAL      *INLR = *ON                                  
        ****************** End of data ********************************************
```

これを実行すると以下のような結果となります。

```
DSPLY  03-1234-5678   OK     
DSPLY  090-1234-5678  OK     
DSPLY  0120-1234-5678 OK     
DSPLY  0312345678     NG     
DSPLY  09012345678    NG     
DSPLY  012012345678   NG     
```

- PL@TEL : CHECK_TELを実行する際に指定するパラメータリスト
    - P@TOKTEL
        - テスト用の電話番号を格納するパラメータ
        - 後々、得意先マスタの電話番号をセットする予定なのでTOKTELという名称にしています。
    - P@RTCD
        - リターンコードをセットするためのパラメータ
        - ここには`CHECK_TEL`によって`OK`/`NG`の文字列がセットされます。

```shell
0014.00  * パラメータリストの定義                                                
0015.00 C     PL@TEL        PLIST                                                  
0016.00 C                   PARM                    P@TOKTEL         15            
0017.00 C                   PARM                    P@RTCD            2
```

配列に定義した６つの電話番号を繰り返し処理によって１つずつCHECK_TELに渡し、チェック処理の結果をDSPLYで表示しています。
DSPLYで出力する文字列は、パラメータで指定した電話番号と、その電話番号でチェックした結果（OK/NG）を出力したかったので、CATで文字列結合してから出力するようにしました。
CATだと２つの文字列を１つに結合するしかできないのが面倒ですね；
できれば　`電話番号→結果`と、矢印なんかも付けて、テスト結果を整形して出力したかったのですが、面倒だったのでやめました。

```shell
0019.00  * 電話番号の正規表現チェック処理実行                                    
0020.00 C                   FOR       i = 1 to 6 by 1                              
0021.00 C                   EVAL      P@TOKTEL = @@TELS(i)                         
0022.00 C                   EVAL      P@RTCD = ''                                  
0023.00 C                   CALL      'CHECK_TEL'   PL@TEL                         
0024.00 C     @@TELS(i)     CAT       P@RTCD        str                            
0025.00 C     str           DSPLY                                                  
0026.00 C                   ENDFOR
```

とりあえず、これで電話番号のチェック処理の実装ができたので、得意先マスタメンテナンス画面の実装も、あとは残りのチェック処理の実装ちょろっとと、登録／更新／削除処理の実装だけなんで、もう少ししたらQiita記事に投稿できるかな。。。