C言語で文字列操作を行う際、「特定の単語が含まれているか」だけなら strstr 関数で十分ですが、「郵便番号の形式かチェックしたい」「小数点を含む数値だけを取り出したい」といった複雑なパターンマッチングが必要な場面では、正規表現の出番となります。
しかし、標準的な環境(LinuxやmacOSなどのPOSIX準拠システム)であれば、<regex.h> ライブラリを使用することで強力な正規表現機能を利用できます。
この記事では、C言語における正規表現の基本的な使い方から、一致した部分の文字列抽出、浮動小数点数の判定、そしてC言語特有のロケール設定やメモリ管理の注意点まで、サンプルコード付きで徹底解説します。
![]() 執筆者:マヒロ |
|
C言語で正規表現を扱うための基礎知識
C言語で正規表現を扱うには、主に POSIX正規表現ライブラリ である <regex.h> を使用します。
このライブラリを使うための基本的な流れは、以下の3ステップです。
- コンパイル (
regcomp): 文字列のパターンを、コンピュータが処理できる形式に変換する。 - 実行 (
regexec): コンパイルしたパターンを使って、対象の文字列を検索・判定する。 - 解放 (
regfree): 使い終わったメモリを解放する。
この「コンパイル」と「解放」の手順が必要な点が、他の軽量言語と大きく異なるポイントです。
【重要】ロケール(言語設定)について
C言語の正規表現関数は、実行環境のロケール設定に依存します。
特に日本語などのマルチバイト文字を扱う場合、ロケールを適切に設定しないと、バイト単位での誤判定や文字化けが発生するリスクがあります。
プログラムの冒頭で必ず <locale.h> をインクルードし、setlocale(LC_ALL, ""); を実行して、システムのデフォルトロケールを適用するようにしましょう。
主要な関数と定数一覧
まずは、プログラミングで頻繁に使用する関数とフラグ(定数)を整理しておきましょう。
| 関数名 | 役割 |
|---|---|
| regcomp | 正規表現パターンをコンパイルする |
| regexec | コンパイルされたパターンで文字列検索を実行する |
| regfree | 使用した正規表現オブジェクトのメモリを解放する |
| regerror | エラーが発生した際にエラーメッセージを取得する |
コンパイル時によく使うフラグ
REG_EXTENDED: 拡張正規表現を使用する(+や?などを使う場合に必須)。REG_ICASE: 大文字と小文字を区別しない。REG_NOSUB: マッチした位置情報を報告しない(判定だけしたい場合に高速)。
よく使う正規表現パターン一覧
C言語に限った話ではありませんが、記述時によく使うメタ文字も確認しておきましょう。
C言語の文字列内で書く場合、バックスラッシュ \ はエスケープが必要(\\ と書く)な点に注意してください。
^: 行頭$: 行末.: 任意の1文字*: 直前の文字が0回以上繰り返し+: 直前の文字が1回以上繰り返し(要REG_EXTENDED)?: 直前の文字が0回または1回(要REG_EXTENDED)[0-9]: 数字[a-z]: 英小文字
実践!正規表現で文字列を検索・判定する
それでは、実際にコードを書いてみましょう。
まずは最も基本的な「文字列がパターンに一致するかどうか」を判定するプログラムです。
ここでは、入力された文字列が「数字のみで構成されているか」をチェックします。
基本的なマッチングのサンプルコード
#include <stdio.h>
#include <regex.h>
#include <locale.h> // ロケール設定に必要
int main(void) {
// ロケールをシステムのデフォルトに設定(日本語環境などで重要)
setlocale(LC_ALL, "");
const char *target = "12345"; // 判定したい文字列
const char *pattern = "^[0-9]+$"; // 正規表現パターン(数字のみ)
regex_t reg;
int ret;
char msgbuf[100];
// 1. 正規表現のコンパイル
// REG_EXTENDED: 拡張正規表現を有効化(+を使うため)
// REG_NOSUB: 位置情報は不要(判定のみ)
ret = regcomp(®, pattern, REG_EXTENDED | REG_NOSUB);
if (ret != 0) {
fprintf(stderr, "コンパイルエラー\n");
return 1;
}
// 2. マッチングの実行
ret = regexec(®, target, 0, NULL, 0);
if (!ret) {
printf("マッチしました: '%s' は数字のみです。\n", target);
} else if (ret == REG_NOMATCH) {
printf("マッチしませんでした。\n");
} else {
// エラー処理
regerror(ret, ®, msgbuf, sizeof(msgbuf));
fprintf(stderr, "実行エラー: %s\n", msgbuf);
}
// 3. メモリの解放
regfree(®);
return 0;
}
実行結果
マッチしました: '12345' は数字のみです。
ソースコードの内容を詳しく解説します。
まず、setlocale でロケールを設定します。
次に、regcomp 関数を使って正規表現パターン "^[0-9]+$" をコンパイルしています。
この際、REG_EXTENDED を指定することで +(1回以上の繰り返し)が使えるようになります。
次に、regexec 関数で実際の検索を行います。
戻り値が 0 であればマッチ成功、REG_NOMATCH であればマッチ失敗です。
最後に、regfree を呼び出して、regcomp で確保されたメモリ領域を必ず解放します。
これを忘れるとメモリリークの原因になるため、C言語では必須の作法です。
マッチした文字列を抽出するテクニック
単なる判定だけでなく、「文字列の中から日付部分を取り出したい」あるいは「浮動小数点数だけを抽出したい」というケースも多いでしょう。
C言語で抽出を行うには、regmatch_t 構造体を使用します。
regmatch_t構造体で位置を特定する
regexec の引数に regmatch_t 配列を渡すことで、マッチした部分の「開始位置」と「終了位置」を取得できます。
これを利用して、元の文字列から該当部分を切り出します。
浮動小数点数を抽出するサンプルコード
文章の中から、小数(例: -3.14 や .5)を抽出して表示するプログラムです。
ここでは、負の数や整数部が省略された小数にも対応するパターンを使用し、無限ループ対策も実装しています。
#include <stdio.h>
#include <regex.h>
#include <locale.h>
int main(void) {
setlocale(LC_ALL, "");
const char *target = "Value is -3.14159, and ratio is .5.";
// 浮動小数点数のパターン:
// [-+]? : 符号(省略可)
// (...|...) : グループ化
// [0-9]+\\.[0-9]* : 数字 + ドット + 数字(省略可、例: 3.)
// \\.[0-9]+ : ドット + 数字(例: .5)
const char *pattern = "[-+]?([0-9]+\\.[0-9]*|\\.[0-9]+)";
regex_t reg;
regmatch_t pmatch[1]; // マッチ結果を格納する配列(全体マッチ用)
int ret;
// コンパイル(位置情報が必要なので REG_NOSUB は指定しない)
if (regcomp(®, pattern, REG_EXTENDED) != 0) {
fprintf(stderr, "コンパイルエラー\n");
return 1;
}
// 検索実行
const char *p = target;
while (1) {
// ※ループ2回目以降、行頭指定(^)を無効にする REG_NOTBOL を検討する場合もありますが
// 今回のパターンには ^ が含まれていないため省略します。
ret = regexec(®, p, 1, pmatch, 0);
if (ret == REG_NOMATCH) break; // マッチしなくなったら終了
int start = pmatch[0].rm_so;
int end = pmatch[0].rm_eo;
int len = end - start;
// 開始位置と長さを使って表示
// %.*s は、長さを指定して文字列を表示するテクニック
printf("抽出された数値: %.*s\n", len, p + start);
// 次の検索のためにポインタを進める
// 【重要】無限ループ対策
// マッチ長さが0の場合(空文字マッチなど)は強制的に1文字進める
if (len == 0) {
p++;
if (*p == '\0') break; // 文字列終端なら終了
} else {
p += end;
}
}
regfree(®);
return 0;
}
実行結果
抽出された数値: -3.14159
抽出された数値: .5
ポイントは、regexec の第3引数と第4引数です。
ここに「取得したいマッチの数」と「結果を格納する構造体配列(pmatch)」を渡します。
マッチが成功すると、pmatch[0].rm_so に開始オフセット、pmatch[0].rm_eo に終了オフセットが格納されます。
printf("%.*s", length, string) という書式指定を使うことで、わざわざバッファにコピーしなくても、指定した長さ分だけ文字列を表示することができます。
また、ループ内でポインタを進める際、マッチした長さが0の場合の対策を行っています。
正規表現によっては空文字にマッチすることがあり、その場合にポインタが進まず無限ループに陥るのを防ぐためです。
C言語での「置換」処理について
Pythonなどの他言語には sub や replace といった置換関数が用意されていますが、残念ながら C言語の <regex.h> には置換機能がありません。
そのため、正規表現で置換を行いたい場合は、以下の手順で自作する必要があります。
regexecでマッチした位置(開始・終了)を取得する。- マッチした箇所の「前」までの文字列を新しいバッファにコピーする。
- 置換したい文字列をバッファに追加する。
- マッチした箇所の「後」の文字列をバッファに追加する。
非常に手間がかかるため、単純な固定文字列の置換であれば、正規表現を使わずに自前のループ処理や strstr を組み合わせて実装する方が、パフォーマンス的にもコードの量的にも有利な場合があります。
C言語のスキルを活かして年収を上げる方法
以上、C言語の正規表現の使い方について解説してきました。
なお、C言語のスキルがある場合には、「転職して年収をアップさせる」「副業で稼ぐ」といった方法を検討するのがおすすめです。
C言語を扱えるエンジニアは比較的希少価値が高く、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。
なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。
併せて、副業案件を獲得できるエージェントにも登録しておくと、空いている時間を活かして稼げるようなC言語の案件を探しやすくなります。
転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。



