Qt開発者必見:QRegExp::setCaseSensitivityを使った正規表現の具体例

2025-05-27

QRegExp::setCaseSensitivity() は、Qtの正規表現クラスである QRegExp オブジェクトが文字列を照合(マッチング)する際に、大文字・小文字を区別するかどうかを設定するための関数です。

正規表現は、特定のパターンに一致する文字列を検索したり、検証したりするために使われます。例えば、「apple」という文字列を検索する正規表現があった場合、デフォルトでは「Apple」や「APPLE」とは一致しません。このような場合に、setCaseSensitivity() を使って大文字・小文字の区別を「しない」ように設定することで、「apple」「Apple」「APPLE」など、大文字・小文字が混在していてもすべて一致するようにすることができます。

引数

この関数は、Qt::CaseSensitivity 型の引数を1つ取ります。この列挙型には以下の2つの値があります。

  • Qt::CaseInsensitive: 大文字・小文字を区別せずに照合します。
  • Qt::CaseSensitive: 大文字・小文字を区別して照合します。これがデフォルトの動作です。

使用例

#include <QRegExp>
#include <QString>
#include <QDebug>

int main() {
    QString text = "Apple, apple, APPLE";

    // 1. 大文字・小文字を区別する場合 (デフォルト)
    QRegExp regex1("apple");
    // setCaseSensitivity() を呼び出さなくてもデフォルトで Qt::CaseSensitive です
    // regex1.setCaseSensitivity(Qt::CaseSensitive); 

    qDebug() << "Case sensitive matches:";
    int pos1 = 0;
    while ((pos1 = regex1.indexIn(text, pos1)) != -1) {
        qDebug() << "  Found at index:" << pos1 << " matched text:" << regex1.capturedTexts().first();
        pos1 += regex1.matchedLength();
    }
    // 出力例:
    // Case sensitive matches:
    //   Found at index: 7 matched text: "apple"

    // 2. 大文字・小文字を区別しない場合
    QRegExp regex2("apple");
    regex2.setCaseSensitivity(Qt::CaseInsensitive); // ここで大文字・小文字を区別しない設定にする

    qDebug() << "\nCase insensitive matches:";
    int pos2 = 0;
    while ((pos2 = regex2.indexIn(text, pos2)) != -1) {
        qDebug() << "  Found at index:" << pos2 << " matched text:" << regex2.capturedTexts().first();
        pos2 += regex2.matchedLength();
    }
    // 出力例:
    // Case insensitive matches:
    //   Found at index: 0 matched text: "Apple"
    //   Found at index: 7 matched text: "apple"
    //   Found at index: 14 matched text: "APPLE"

    return 0;
}

注意点

Qt 5以降では、より強力で最新の正規表現エンジンである QRegularExpression の使用が推奨されています。QRegExp はQt 5で非推奨(deprecated)となり、Qt 6ではQt Core Compatibilityモジュールの一部として提供されていますが、新しいプロジェクトでは QRegularExpression を検討することをお勧めします。



QRegExp::setCaseSensitivity() 自体は非常にシンプルな関数であり、直接的なエラーの原因となることは稀です。しかし、この設定が期待通りの正規表現マッチング結果をもたらさない場合、それは通常、以下のいずれかの問題に起因します。

想定外の大文字・小文字の区別

問題
setCaseSensitivity(Qt::CaseInsensitive) を設定したはずなのに、なぜか大文字・小文字を区別してマッチングされてしまう、またはその逆で、区別したいのに区別されない。

原因

  • QRegExpオブジェクトの再初期化
    QRegExp オブジェクトを一度作成し、setCaseSensitivity() で設定した後、再度正規表現パターンを文字列としてコンストラクタに渡して初期化してしまうと、setCaseSensitivity() で設定した内容がリセットされ、デフォルトの Qt::CaseSensitive に戻ってしまいます。
  • 間違ったQt::CaseSensitivity値の指定
    Qt::CaseSensitiveQt::CaseInsensitive を誤って指定している。
  • 設定の忘れ
    setCaseSensitivity() を呼び出すのを忘れている。

トラブルシューティング

  • デバッグ出力 (qDebug()) を使って、QRegExp オブジェクトの caseSensitivity() の値を確認し、期待通りの設定になっているかを検証します。
  • QRegExp オブジェクトのライフサイクルを確認します。特に、正規表現パターンを変更する場合は、setPattern() を使用し、QRegExp(const QString &pattern) コンストラクタを再度使用しないようにします。
    QRegExp regex("apple");
    regex.setCaseSensitivity(Qt::CaseInsensitive); // OK
    
    // BAD: これをすると、setCaseSensitivity の設定がリセットされる
    // regex = QRegExp("orange");
    
    // GOOD: パターンを変更する場合は setPattern() を使用する
    regex.setPattern("orange");
    
  • コードを注意深くレビューし、setCaseSensitivity() が正しく呼び出され、適切な Qt::CaseSensitivity の値が渡されているかを確認します。

QRegExpの非推奨とQRegularExpressionへの移行の考慮不足

問題
QRegExp を使用しているが、期待通りの複雑な正規表現パターンが機能しない、またはパフォーマンスが悪い。setCaseSensitivity() の設定自体は正しいように見えるが、全体のマッチングがうまくいかない。

原因

  • QRegExpQRegularExpression では、大文字・小文字の区別の設定方法が異なります。
  • QRegExp はQt 5で非推奨となり、Qt 6では互換性モジュールに移動されました。これは、PCRE (Perl Compatible Regular Expressions) に基づくより強力で現代的な正規表現エンジンである QRegularExpression に置き換えられたためです。QRegExp は一部の高度な正規表現機能(後方参照の複雑なケース、非貪欲マッチなど)をサポートしていなかったり、予期せぬ動作をすることがあります。

トラブルシューティング

  • QRegularExpression で大文字・小文字の区別を設定する場合は、コンストラクタの QRegularExpression::PatternOptions を使用するか、setPatternOptions() 関数に QRegularExpression::CaseInsensitiveOption を渡します。
    // QRegExp (非推奨)
    QRegExp rx("pattern");
    rx.setCaseSensitivity(Qt::CaseInsensitive);
    
    // QRegularExpression (推奨)
    QRegularExpression re("pattern", QRegularExpression::CaseInsensitiveOption);
    // または
    // QRegularExpression re("pattern");
    // re.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
    
  • 可能な限り、QRegExp から QRegularExpression への移行を検討します。これにより、より堅牢で予測可能な正規表現処理が期待できます。

正規表現パターンの間違い

問題
setCaseSensitivity() を正しく設定しても、マッチングが期待通りにいかない。

原因

  • setCaseSensitivity() の設定は正しいが、そもそも正規表現パターン自体が間違っている。例えば、特殊文字のエスケープを忘れている、文字クラスの定義が間違っている、量指定子の使い方が不適切など。

トラブルシューティング

  • 正規表現テスターツール(オンラインのRegExrやRegex101など)を使用して、パターンが期待通りの文字列と一致するかを確認します。これらのツールでは、大文字・小文字の区別のオプションも設定できます。
  • 正規表現パターンを単純化してテストし、問題が setCaseSensitivity() 以外の部分にあるかを特定します。

文字エンコーディングの問題

問題
特定の文字(非ASCII文字など)で大文字・小文字の区別が期待通りに機能しない。

原因

  • 文字列のエンコーディングと、正規表現エンジンが文字を解釈するエンコーディングとの不一致。Qtの文字列 (QString) は通常Unicodeを使用しますが、OSのロケール設定やファイルのエンコーディングによっては問題が発生する可能性があります。
  • QRegExpQRegularExpression が、使用している文字セットと互換性があることを確認します。通常、これらはUnicodeを適切に処理します。
  • 可能な限り、すべての文字列がUnicode (QString) で正しく扱われていることを確認します。


QRegExp はQt 5で非推奨となり、Qt 6ではQt Core Compatibilityモジュールの一部として提供されていますが、ここではその基本的な機能を示すために QRegExp を使用します。新しいプロジェクトでは QRegularExpression の使用を強く推奨します。

以下の例では、QRegExp を使用して文字列の中から特定のパターンを検索し、setCaseSensitivity() が検索結果にどのように影響するかを示します。

#include <QCoreApplication>
#include <QRegExp>
#include <QString>
#include <QStringList>
#include <QDebug> // デバッグ出力用

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString text = "Apple, apple, APPLE. This is an APple.";

    qDebug() << "--- 大文字・小文字を区別する (Qt::CaseSensitive) ---";
    // 1. 大文字・小文字を区別する (Qt::CaseSensitive) - デフォルトの動作
    QRegExp regexCaseSensitive("apple");
    // setCaseSensitivity() を明示的に呼び出さなくても、これがデフォルトです
    regexCaseSensitive.setCaseSensitivity(Qt::CaseSensitive);

    int pos = 0;
    qDebug() << "検索対象文字列: " << text;
    qDebug() << "正規表現パターン: " << regexCaseSensitive.pattern();

    while ((pos = regexCaseSensitive.indexIn(text, pos)) != -1) {
        qDebug() << "  マッチが見つかりました (インデックス:" << pos << ", テキスト:" << regexCaseSensitive.capturedTexts().first() << ")";
        pos += regexCaseSensitive.matchedLength(); // 次の検索開始位置を更新
    }
    // 出力例:
    // --- 大文字・小文字を区別する (Qt::CaseSensitive) ---
    // 検索対象文字列: "Apple, apple, APPLE. This is an APple."
    // 正規表現パターン: "apple"
    //   マッチが見つかりました (インデックス:7, テキスト:"apple")


    qDebug() << "\n--- 大文字・小文字を区別しない (Qt::CaseInsensitive) ---";
    // 2. 大文字・小文字を区別しない (Qt::CaseInsensitive)
    QRegExp regexCaseInsensitive("apple");
    regexCaseInsensitive.setCaseSensitivity(Qt::CaseInsensitive); // ここで設定

    pos = 0; // 検索開始位置をリセット
    qDebug() << "検索対象文字列: " << text;
    qDebug() << "正規表現パターン: " << regexCaseInsensitive.pattern();

    while ((pos = regexCaseInsensitive.indexIn(text, pos)) != -1) {
        qDebug() << "  マッチが見つかりました (インデックス:" << pos << ", テキスト:" << regexCaseInsensitive.capturedTexts().first() << ")";
        pos += regexCaseInsensitive.matchedLength(); // 次の検索開始位置を更新
    }
    // 出力例:
    // --- 大文字・小文字を区別しない (Qt::CaseInsensitive) ---
    // 検索対象文字列: "Apple, apple, APPLE. This is an APple."
    // 正規表現パターン: "apple"
    //   マッチが見つかりました (インデックス:0, テキスト:"Apple")
    //   マッチが見つかりました (インデックス:7, テキスト:"apple")
    //   マッチが見つかりました (インデックス:14, テキスト:"APPLE")
    //   マッチが見つかりました (インデックス:30, テキスト:"APple")


    qDebug() << "\n--- setPattern() でパターンを変更し、設定を保持する ---";
    // 3. setPattern() を使ってパターンを変更し、CaseSensitivityの設定が保持されることを確認
    QRegExp regexPreserveSetting("banana");
    regexPreserveSetting.setCaseSensitivity(Qt::CaseInsensitive); // 大文字・小文字を区別しない設定
    qDebug() << "初期パターン: " << regexPreserveSetting.pattern() << ", ケース感度: " << regexPreserveSetting.caseSensitivity();

    regexPreserveSetting.setPattern("orange"); // setPattern() でパターンを変更
    qDebug() << "変更後のパターン: " << regexPreserveSetting.pattern() << ", ケース感度: " << regexPreserveSetting.caseSensitivity();

    QString newText = "Orange Juice, oRANGE";
    pos = 0;
    qDebug() << "検索対象文字列: " << newText;

    while ((pos = regexPreserveSetting.indexIn(newText, pos)) != -1) {
        qDebug() << "  マッチが見つかりました (インデックス:" << pos << ", テキスト:" << regexPreserveSetting.capturedTexts().first() << ")";
        pos += regexPreserveSetting.matchedLength();
    }
    // 出力例:
    // --- setPattern() でパターンを変更し、設定を保持する ---
    // 初期パターン: "banana", ケース感度: 1 (Qt::CaseInsensitive)
    // 変更後のパターン: "orange", ケース感度: 1 (Qt::CaseInsensitive)
    // 検索対象文字列: "Orange Juice, oRANGE"
    //   マッチが見つかりました (インデックス:0, テキスト:"Orange")
    //   マッチが見つかりました (インデックス:14, テキスト:"oRANGE")


    qDebug() << "\n--- コンストラクタで直接指定する ---";
    // 4. コンストラクタで直接 CaseSensitivity を指定する
    QRegExp regexFromConstructor("grape", Qt::CaseInsensitive);
    QString fruitText = "Grapefruit, GrapE, grape";
    pos = 0;
    qDebug() << "検索対象文字列: " << fruitText;
    qDebug() << "正規表現パターン: " << regexFromConstructor.pattern();
    qDebug() << "ケース感度: " << regexFromConstructor.caseSensitivity();

    while ((pos = regexFromConstructor.indexIn(fruitText, pos)) != -1) {
        qDebug() << "  マッチが見つかりました (インデックス:" << pos << ", テキスト:" << regexFromConstructor.capturedTexts().first() << ")";
        pos += regexFromConstructor.matchedLength();
    }
    // 出力例:
    // --- コンストラクタで直接指定する ---
    // 検索対象文字列: "Grapefruit, GrapE, grape"
    // 正規表現パターン: "grape"
    // ケース感度: 1 (Qt::CaseInsensitive)
    //   マッチが見つかりました (インデックス:0, テキスト:"Grape")
    //   マッチが見つかりました (インデックス:12, テキスト:"GrapE")
    //   マッチが見つかりました (インデックス:19, テキスト:"grape")

    return a.exec();
}

コードのコンパイルと実行方法

このコードをコンパイルして実行するには、Qt開発環境が設定されている必要があります。

  1. .pro ファイルの作成 (例: regex_example.pro):

    QT += core
    SOURCES += main.cpp
    
  2. main.cpp ファイルの作成: 上記のC++コードを main.cpp として保存します。

  3. QMakeとビルド: ターミナルで以下のコマンドを実行します。

    qmake -qt=qt5 regex_example.pro # Qt 5を使用する場合
    # または qmake regex_example.pro # 環境変数PATHにQtのbinディレクトリが設定されていれば通常はこれでOK
    make # Linux/macOS
    # nmake または jom # Windows (Visual Studioの場合)
    
  4. 実行:

    ./regex_example # Linux/macOS
    # debug/regex_example.exe または release/regex_example.exe # Windows
    


前述の通り、QRegExp はQt 5で非推奨となり、Qt 6ではQt Core Compatibilityモジュールに移動されました。新しいコードでは、より強力で柔軟な QRegularExpression クラスを使用することが強く推奨されます。

そのため、代替手段としては主に以下の2つが挙げられます。

  1. QRegularExpression の使用(最も推奨される代替手段)
  2. 正規表現パターン内で大文字・小文字の区別を制御する(通常はQRegularExpressionと組み合わせる)

QRegularExpression の使用 (推奨)

QRegularExpression はPerl互換の正規表現 (PCRE) エンジンに基づいており、より豊富な機能と優れたパフォーマンスを提供します。QRegExp::setCaseSensitivity() に相当する機能は、QRegularExpression::PatternOption 列挙型を使って設定します。

QRegularExpression::CaseInsensitiveOption

QRegularExpression で大文字・小文字を区別しないマッチングを行うには、QRegularExpression::CaseInsensitiveOption を使用します。これは、QRegularExpression オブジェクトのコンストラクタに渡すか、setPatternOptions() 関数で設定できます。

A. コンストラクタで設定する方法

#include <QRegularExpression>
#include <QString>
#include <QDebug>

int main() {
    QString text = "Apple, apple, APPLE. This is an APple.";

    // 大文字・小文字を区別しない正規表現オブジェクトを作成
    QRegularExpression re("apple", QRegularExpression::CaseInsensitiveOption);

    qDebug() << "検索対象文字列: " << text;
    qDebug() << "正規表現パターン: " << re.pattern();
    qDebug() << "パターンオプション: " << re.patternOptions(); // デバッグでオプション値を確認

    QRegularExpressionMatch match = re.match(text);
    while (match.hasMatch()) {
        qDebug() << "  マッチが見つかりました (インデックス:" << match.capturedStart() << ", テキスト:" << match.captured(0) << ")";
        match = re.match(text, match.capturedEnd()); // 次の検索開始位置を更新
    }
    // 出力例:
    // 検索対象文字列: "Apple, apple, APPLE. This is an APple."
    // 正規表現パターン: "apple"
    // パターンオプション: QRegularExpression::CaseInsensitiveOption(1)
    //   マッチが見つかりました (インデックス:0, テキスト:"Apple")
    //   マッチが見つかりました (インデックス:7, テキスト:"apple")
    //   マッチが見つかりました (インデックス:14, テキスト:"APPLE")
    //   マッチが見つかりました (インデックス:30, テキスト:"APple")

    return 0;
}

B. setPatternOptions() で設定する方法

既存の QRegularExpression オブジェクトのオプションを変更する場合に便利です。

#include <QRegularExpression>
#include <QString>
#include <QDebug>

int main() {
    QString text = "Hello, World. hello, QT.";

    QRegularExpression re("hello"); // デフォルトでは大文字・小文字を区別する

    qDebug() << "初期オプション (デフォルト): " << re.patternOptions();
    QRegularExpressionMatch match1 = re.match(text);
    if (match1.hasMatch()) {
        qDebug() << "  初期マッチ: " << match1.captured(0); // "hello" のみマッチ
    } else {
        qDebug() << "  初期マッチなし。";
    }

    re.setPatternOptions(QRegularExpression::CaseInsensitiveOption); // 大文字・小文字を区別しない設定に変更
    qDebug() << "変更後のオプション: " << re.patternOptions();

    QRegularExpressionMatch match2 = re.match(text);
    while (match2.hasMatch()) {
        qDebug() << "  変更後のマッチ: " << match2.captured(0) << " at index " << match2.capturedStart();
        match2 = re.match(text, match2.capturedEnd());
    }
    // 出力例:
    // 初期オプション (デフォルト): QRegularExpression::PatternOptions(NoPatternOption)
    //   初期マッチ: "hello"
    // 変更後のオプション: QRegularExpression::CaseInsensitiveOption(1)
    //   変更後のマッチ: "Hello" at index 0
    //   変更後のマッチ: "hello" at index 13

    return 0;
}

複数のオプションの組み合わせ

QRegularExpression::PatternOption はビットフラグであるため、複数のオプションを | (OR) 演算子で組み合わせて指定できます。

#include <QRegularExpression>
#include <QDebug>

int main() {
    QString text = "Line one\nline TWO\nLine three";

    // 大文字・小文字を区別せず、かつ複数行モードで検索
    QRegularExpression re("^line", QRegularExpression::CaseInsensitiveOption | QRegularExpression::MultilineOption);

    QRegularExpressionMatch match = re.match(text);
    while (match.hasMatch()) {
        qDebug() << "マッチ: " << match.captured(0);
        match = re.match(text, match.capturedEnd());
    }
    // 出力例:
    // マッチ: "Line"
    // マッチ: "line"
    // マッチ: "Line"

    return 0;
}

これは、QRegExp でも QRegularExpression でも利用できる、正規表現言語自体の機能です。特定のパターンに対してのみ大文字・小文字の区別を変更したい場合に便利です。

(?i)(?-i)

  • (?-i): この位置から後続のパターンに対して大文字・小文字を区別するマッチングを有効にします。
  • (?i): この位置から後続のパターンに対して大文字・小文字を区別しないマッチングを有効にします。
#include <QRegularExpression> // QRegExpでも同様に機能します
#include <QString>
#include <QDebug>

int main() {
    QString text = "Apple, apple, Banana, BANANA";

    // `(?i)` を使ってパターンの一部だけ大文字・小文字を区別しないようにする
    // "apple" は区別しない、"Banana" は区別する
    QRegularExpression re("(?i)apple(?-i)|Banana");

    QRegularExpressionMatch match = re.match(text);
    while (match.hasMatch()) {
        qDebug() << "マッチ: " << match.captured(0) << " at index " << match.capturedStart();
        match = re.match(text, match.capturedEnd());
    }
    // 出力例:
    // マッチ: "Apple" at index 0
    // マッチ: "apple" at index 7
    // マッチ: "Banana" at index 14

    qDebug() << "\n--- 全体を区別しないパターン (`(?i:...)`) ---";
    // `(?i:...)` を使って特定のグループに対してのみ大文字・小文字を区別しない
    QRegularExpression re2("(?i:apple|banana)");
    QString text2 = "Apple, BANANA, Orange";
    match = re2.match(text2);
    while (match.hasMatch()) {
        qDebug() << "マッチ: " << match.captured(0) << " at index " << match.capturedStart();
        match = re2.match(text2, match.capturedEnd());
    }
    // 出力例:
    // --- 全体を区別しないパターン (`(?i:...)`) ---
    // マッチ: "Apple" at index 0
    // マッチ: "BANANA" at index 7


    return 0;
}
  • 正規表現パターン内での (?i)(?-i) を使う方法は、より細かい制御が必要な場合に便利ですが、通常は QRegularExpression::CaseInsensitiveOption を使用する方がシンプルです。
  • QRegularExpression で大文字・小文字の区別を設定するには、QRegularExpression::CaseInsensitiveOption をコンストラクタまたは setPatternOptions() で指定します。
  • 新しいコードでは QRegularExpression を使用するべきです。 QRegExp は非推奨であり、機能とパフォーマンスの面で劣ります。