事の起こり
C++を用いて文字列のフォーマットチェックを行いたいけど、for文を用いて一文字ずつチェックするのは面倒。そんな面倒を解決するため、正規表現を用いたフォーマットチェックについて学習したので自分のための忘備録を残すことにしました。
電話番号のフォーマットチェックについて考えて見よう
突然ですが、皆さん。次の3つの文字列の内、「電話番号」に該当するものを選んでみてくだい。
①999-9383-38391
②999-3393-9856
③90933-480-222
正解は②番です。簡単ですね。しかしここで、②が電話番号である条件について考えてみましょう。答えは次の通りです。
(1) 数字と'-'合わせてぴったり13桁
(2) 3文字目が'-'
(3) 8文字目が'-'
(4) 3,8文字目以外は必ず数字である。かつ、その数値は'0'以上'9'以下である。
以外と複雑ですね。 この条件の通りにフォーマットチェックして文字列が携帯電話番号か否かを判定する関数は次のようになります。(あくまで、私が考える方法なので読み飛ばしてもらって結構です。)
bool judge_tell_num(string tell_num){
int num = 0;
if(tell_num.length() == 13){//(1)ぴったり13桁か否か?
for(int i = 0;i<=tell_num.length();i++){//一文字ずつ条件判定
if(i==3 || i ==8){//(2),(3)3文字目と8文字目が'-'か否か?
if(tell_num[i] == '-'){
num++;
}else{};
}else{//(4)3,8文字以外は数字か?
if(tell_num[i] >= '0' && tell_num[i] <= '9'){
num++;
}else{};
}
}
}else{//13桁でなければfalseを返す。
return false;
}
//13桁それぞれが指定の条件を満たしていたら、trueを返す。
if(num == 13){
return true;
}else{
return false;
}
}
汚いコードですよね。条件判定の順番を工夫すればもっと読みやすく、無駄なく書けるはずです。
本当は、誰が読んでも理解できるようにフォーマットチェックの処理を書きたい。でも、フォーマットチェックに時間をかけたくない。そんな時は、正規表現とregexオブジェクトを使えば楽に分かりやすくフォーマットチェック処理が書けます。
正規表現とregexオブジェクト用いてフォーマットチェックをしよう
いきなりですが、正規表現を用いて先ほどの電話番号を表現すると次のようになります。
・携帯電話(999-3393-9856など)の番号の正規表現
① \d\d\d-\d\d\d\d-\d\d\d\d または ② \d{3}-\d{4}-\d{4}
「\d」がたくさん並んでますね。この「\d」は正規表現において「数字」を表しています。「\d{n}」はn個の連続する「数字」を表しています。「-」はそのまま「-」を表します。要は、
「3つの数字の連続」-「4つの数字の連続」-「3つの数字の連続」が携帯電話番号の正規表現である
と言うことを意味しているわけです。ここまで、表現できれば後は簡単で、文字列がこの正規表現に合っているか調べれば、その文字列が携帯電話番号であるかが判別できます。C++ではこのような正規表現を用いた判別をregexライブラリで行うことができます。次のコードがその一例です。(\d{3}-\d{4}-\d{4}がR"()"で括られているのは\d{3}-\d{4}-\d{4}が生文字リテラルであることを表すためです。)
#include <iostream>
#include <regex>
using namespace std;
int main() {
string tell_num = "099-9999-9999";//電話番号
regex format (R"(\d{3}-\d{4}-\d{4})");//regexオブジェクトで正規表現を用いた一般化
bool result=regex_match(tell_num,format);//フォーマットチェックの結果を返す
if(result == true){
cout << "電話番号 形式OK" <<endl;
}else{
cout << "電話番業 形式 NG" <<endl;
}
このコードのregex format (R"(\d{3}-\d{4}-\d{4})")
はregex 変数名 (正規表現)
と言う構文になっていて、要はformatが指す正規表現は\d{3}-\d{4}-\d{4}であることと言う意味です。(極端な話、int a
とかとかの変数宣言と一緒です)
次のregex_match(tell_num,format)
はregex_match(対象文字列,正規表現)
と言う構文になっていて、対象文字列が正規表現で表したフォーマットと一致している時、tureを返し、そうでない時にfalseを返すと言うregexライブラリで提供される関数です。そして、この関数の戻り値から、任意の文字列が携帯電話番号であるかを判別しています。
こんな感じで、正規表現とregexを用いて簡単に携帯電話番号か否かの判別を行えるようになりました!!!便利ですね。
まとめ
C++で文字列のフォーマットチェックをする時は、我流の処理を書くより正規表現とregexオブジェクト使った方がはるかに楽だと感じました。今回の例では「数字」を表す「\d」しか正規表現を紹介していませんが、数字を表す以外にも「任意の文字」を表す「.」など様々な正規表現が存在するので興味のある方は調べてみてください。また、regexライブラリには文字列の検証を行うregex_match以外にも正規表現に従った文字列の置換を行うregex_replaceや正規表現で文字列中の部分文字を検索するregex_iteratorなど様々な便利な関数が提要されていますので、こちらも調べてみると役に立つと思います。