正規表現std::regex[C++11]
正規表現(Regular Expression)も、プログラミングの現場ではよく使われますね。正規表現など知るよしもなかったころは、文字関数や文字列関数をゴリゴリ使ってパターンマッチングを書いていたものですが……。今は、どんな言語でも正規表現が扱えるのが普通なので、C++ 11で早々に正規表現std::regexが導入されました。
基本的な使い方を見てみましょう。
#include <regex> …略… regex re(R"(\d+)"); cout << regex_match("1234", re) << endl; // 実行結果:1 cout << regex_match("ABCD", re) << endl; // 実行結果:0
std::regexを使うには、ヘッダファイル<regex>をインクルードします。regexオブジェクトをコンストラクタ引数にパターンを指定して生成し、regex_match関数でマッチングを実行します。結果はfalse(一致なし)かtrue(一致あり)で返ります。
このとき、コンストラクタの引数に指定されているのは、同じくC++ 11で使えるようになった生文字列リテラルです。これは第6回で紹介しましたね。バックスラッシュのエスケープが不要なので、バックスラッシュを多用する正規表現ではとても便利です。
マッチした文字列を取得する例も示します。ここでも、regexオブジェクトを作るのは同じで、引数を3つとるregex_match関数のオーバーロードを呼び出して、パターンマッチを実行します。結果は、2番目の引数であるmatchに入ります。
string zip = "225-0002"; smatch match; regex re_zip(R"((\d+)-(\d+))"); if( regex_match(zip, match, re_zip)) { cout << match[0].str() << endl; // 実行結果:225-0002 cout << match[1].str() << endl; // 実行結果:225 cout << match[2].str() << endl; // 実行結果:0002 }
なお、正規表現には互換ライブラリSRELLというものがあって、これを使うとJavaScript(ECMAScript)互換の正規表現をregexと同様に扱えるようです。
時間と時刻std::chrono[C++20]
時間情報を扱うのは面倒なものです。そもそも年月日、時分秒に分かれていますし、閏年もありますし、月の日数が一定でないですし、閏秒なんてものもあります。1週間前の日付を求めよ、なんて簡単そうで難しい(というか面倒)です。このような事情なので、モダンなプログラミング言語では時間情報を容易に取り扱えるライブラリを標準で備えることが多いと思います。
我らがC++でも、C++ 11においてstd::chronoライブラリが導入されました。このchronoライブラリ、C++ 11の時点では高精度な時間計測とか短いスパンでの用途に限定されていましたが、C++ 20においてカレンダー機能が実装されて、長期の日時演算なども可能になりました。ここでは、カレンダー機能に注目してみましょう。
まずは、定番の現在日時の取得です。
#include <chrono> …略… auto now_utc = chrono::system_clock::now(); cout << now_utc << endl; // 実行結果:2024-08-12 06:23:28.538803000
std::chronoを使うには、<chrono>ヘッダファイルをインクルードします。chrono::system_clock名前空間のnow関数を呼び出すと、デフォルトのタイムゾーンすなわちUTCにおける現在日時を取得できます。これから日本時間(JST)を取得するには、TimeZoneクラスオブジェクトをコンストラクタの引数にロケーション文字列(ここでは"Asia/Tokyo")とUTC時刻を指定して生成します。
auto now_jst = chrono::zoned_time{"Asia/Tokyo", now_utc}; cout << now_jst << endl; // 実行結果:2024-08-12 15:23:28.538803000 JST
年月日と時分秒から、日時情報を作成することもできます。
using namespace std::chrono_literals; using std::chrono::August; …略… auto tp = chrono::sys_days{2024y/August/7d} + 16h + 27min + 38s; cout << tp << endl; // 実行結果:2024-08-07 16:27:38
他言語でよく見る、フォーマット文字列や関数の引数に与える形とはずいぶん違いますね。最初のsys_daysについて、これはsys_timeのエイリアスで、ある年月日を表します(Time Pointといいます)。2024y/August/7dは日付のパターンで、2024年8月(August)7日を表します。
ここで、yとかdとかを数値リテラルに付加していますが、これはC++ 11で使えるようになった「ユーザ定義リテラル」というもので、chrono_literals名前空間で定義されています。文字通り、年(y)、日(d)、時(h)、分(min)、秒(s)に相当します。月名(ここではAugust)は、定義済みの定数(8m)です。sys_daysは日付を表しますが、これに時分秒を加算することで、時間情報も含んだ型へと変換されます。
Time Pointからは、年月日時分秒を個別に取り出すこともできます。
auto dp = floor<chrono::days>(tp); auto ymd = chrono::year_month_day{dp}; cout << ymd.month() << " " << ymd.day() << " " << ymd.year() << endl; // 実行結果:Aug 07 2024 auto hms = chrono::hh_mm_ss{tp - dp}; cout << hms.hours() << hms.minutes() << hms.seconds() << endl; // 実行結果:16h27min38s
chronoのCalendar機能はたくさんあるので、よくある処理に絞って紹介しました。