読み込み中...

C++で特殊文字をエスケープして文字列に埋め込む6つの手法

C++のソースコードがを徹底解説するイメージ C++
この記事は約21分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●C++での特殊文字とは?

C++で文字列を扱っていると、時折特殊な文字に出くわすことがあります。

例えば、改行やタブ、ダブルクォーテーションなどがそれに当たります。

これらの特殊文字を文字列内に直接記述しようとすると、コンパイルエラーが発生したり、意図しない動作をしてしまうことがあるのです。

○特殊文字を使う理由

では、なぜ特殊文字を使う必要があるのでしょうか?その理由は、文字列内で特定の機能を表現するためです。

例えば、改行を表現したい場合、単に文字列内で改行を入力しても、C++コンパイラはそれを改行として認識しません。

そこで、特殊な記号を用いて改行を表現する必要があるのです。

○エスケープシーケンスの概要

C++では、特殊文字を表現するために「エスケープシーケンス」という仕組みを使います。

エスケープシーケンスは、バックスラッシュ()に続けて特定の文字を記述することで、特殊な意味を持たせることができます。

例えば、\nは改行を表し、\tはタブを表します。

○サンプルコード1:改行とタブの埋め込み

下記のサンプルコードを見てみましょう。

#include <iostream>
#include <string>

int main() {
    std::string message = "Hello,\nWorld!\tThis is a test.";
    std::cout << message << std::endl;
    return 0;
}

このコードでは、文字列messageに改行(\n)とタブ(\t)を埋め込んでいます。

そして、その文字列をstd::coutで出力しています。

実行結果

Hello,
World!  This is a test.

見ての通り、\nの位置で改行が入り、\tの位置でタブ(スペース)が入っています。

このように、エスケープシーケンスを使うことで、特殊文字を文字列内に埋め込むことができるのです。

●エスケープシーケンスの種類と使い方

さて、C++のエスケープシーケンスには、改行やタブ以外にもさまざまな種類があります。

これらを使いこなせば、文字列内に特殊な文字を埋め込むことができるようになるでしょう。

それぞれのエスケープシーケンスには、独自の役割があります。

例えば、ダブルクォーテーションを文字列内に含めたい場合や、シングルクォーテーションを埋め込みたい場合などです。

バックスラッシュ自体を文字列に含める方法もあります。

ここからは、具体的なエスケープシーケンスとそれぞれの使い方を見ていきましょう。

サンプルコードを交えながら、一つずつ丁寧に説明していきます。

○サンプルコード2:ダブルクォーテーションの埋め込み

C++の文字列リテラルは、ダブルクォーテーション(“)で囲みます。

では、文字列内にダブルクォーテーション自体を含めたい場合はどうすればよいでしょうか?

そんなときは、エスケープシーケンス\”を使います。

#include <iostream>
#include <string>

int main() {
    std::string message = "He said, \"Hello, world!\"";
    std::cout << message << std::endl;
    return 0;
}

このコードでは、文字列内にダブルクォーテーションを埋め込むために、\”を使っています。

バックスラッシュの後にダブルクォーテーションを記述することで、それが特殊文字ではなく、文字列の一部であることを表しているのです。

実行結果

He said, "Hello, world!"

文字列内のダブルクォーテーションが、そのまま出力されていることがわかりますね。

○サンプルコード3:シングルクォーテーションの埋め込み

同様に、文字列内にシングルクォーテーション(‘)を含めたい場合は、エスケープシーケンス\’を使います。

#include <iostream>
#include <string>

int main() {
    std::string message = "It's a beautiful day.";
    std::cout << message << std::endl;
    return 0;
}

このコードでは、文字列内のIt’sのシングルクォーテーションを\’でエスケープしています。

実行結果

It's a beautiful day.

シングルクォーテーションが文字列内に含まれていることがわかります。

○サンプルコード4:バックスラッシュの埋め込み

エスケープシーケンスで使用しているバックスラッシュ()自体を文字列内に含めたい場合は、\を使います。

#include <iostream>
#include <string>

int main() {
    std::string path = "C:\\Users\\YourName\\Documents";
    std::cout << path << std::endl;
    return 0;
}

このコードでは、Windowsのファイルパスを表現するために、バックスラッシュを\でエスケープしています。

実行結果

C:\Users\YourName\Documents

バックスラッシュが文字列内に含まれていることがわかります。

○サンプルコード5:アスキーコードによる特殊文字の表現

エスケープシーケンスを使う別の方法として、アスキーコードを使った特殊文字の表現があります。

アスキーコードは、各文字に割り当てられた番号です。

#include <iostream>
#include <string>

int main() {
    std::string bell = "\x07";
    std::string newline = "\x0A";
    std::cout << "Hello" << bell << " world" << newline;
    return 0;
}

このコードでは、\x07がベル文字(音を鳴らす制御文字)、\x0Aが改行文字を表しています。

\xの後に続く2桁の16進数がアスキーコードを表します。

Hello world

“Hello”と”world”の間でベル音が鳴り、改行が入ります。

●生文字列リテラルの活用

エスケープシーケンスを使った特殊文字の表現は強力ですが、時には煩雑に感じることもあるでしょう。

特に、複数の特殊文字を含む長い文字列を扱う場合は、エスケープシーケンスが頻繁に出現して読みづらくなってしまうかもしれません。

そんなときに活躍するのが、C++11で導入された「生文字列リテラル」です。

生文字列リテラルを使えば、エスケープシーケンスを使わずに特殊文字をそのまま文字列に含めることができます。

これにより、文字列がより自然で読みやすくなるのです。

○生文字列リテラルの概要

生文字列リテラルは、文字列リテラルの前にRを付けることで使用できます。

Rの後にはダブルクォーテーション(“)が続きますが、通常の文字列リテラルとは異なり、閉じるダブルクォーテーションの前に任意の区切り文字を指定することができます。

例えば、次のように書くことができます。

R"(This is a raw string literal.)"

この例では、カッコ()が区切り文字として使われています。

開始の区切り文字と終了の区切り文字で囲まれた範囲が、生文字列リテラルとして扱われます。

○サンプルコード6:生文字列リテラルでの特殊文字の扱い

それでは、生文字列リテラルを使ってみましょう。

#include <iostream>
#include <string>

int main() {
    std::string raw_str = R"(This is a "raw string literal". It can contain \ and \n.)";
    std::cout << raw_str << std::endl;
    return 0;
}

このコードでは、生文字列リテラルの中にダブルクォーテーション(“)、バックスラッシュ()、そして\nを含めています。

通常の文字列リテラルであれば、これらの特殊文字をエスケープする必要がありますが、生文字列リテラルではそのまま記述できるのです。

実行結果

This is a "raw string literal". It can contain \ and \n.

文字列内の特殊文字がそのまま出力されていることがわかります。

これにより、エスケープシーケンスを多用せずに済むので、文字列がよりシンプルで読みやすくなります。

○生文字列リテラルの注意点

ただし、生文字列リテラルにも注意点があります。

生文字列リテラルの中では、エスケープシーケンスが無視されるため、改行をそのまま含めることができてしまいます。

これは時として意図しない動作につながる可能性があるので気をつける必要があります。

また、生文字列リテラルの区切り文字には、カッコ以外にも任意の文字列を使用できます。ただし、区切り文字の選択には注意が必要です。

生文字列リテラルの中に区切り文字と同じ文字列が現れると、意図しない場所で文字列が終了してしまうからです。

生文字列リテラルを使いこなすことで、特殊文字を含む文字列をより簡潔に表現できるようになります。

エスケープシーケンスと生文字列リテラルをうまく使い分けることが、C++での文字列操作のスキルアップにつながるでしょう。

初めは戸惑うかもしれませんが、徐々に慣れていくことが大切です。

実際のコードの中で生文字列リテラルを使ってみて、その利便性を体感してみてください。

特殊文字の扱いに自信が持てるようになれば、C++でのプログラミングがより楽しくなるはずです。

●よくあるエラーと対処法

C++で特殊文字を扱う際によく遭遇するエラーについて見ていきましょう。

これらのエラーは、初心者にとって特につまずきやすいポイントです。

エラーの原因を理解し、適切な対処法を身につけることが、C++でのプログラミングスキルの向上につながります。

○エスケープシーケンスの書き忘れ

特殊文字を文字列に含める際に、エスケープシーケンスの書き忘れは非常によくある間違いです。

例えば、次のようなコードを見てみましょう。

#include <iostream>
#include <string>

int main() {
    std::string message = "Hello, "world"!";
    std::cout << message << std::endl;
    return 0;
}

このコードでは、文字列内のダブルクォーテーション(“)をエスケープしていません。

そのため、コンパイル時にエラーが発生します。

エラーメッセージは、使用しているコンパイラによって異なりますが、一般的には次のようなメッセージが表示されます。

error: missing terminating " character

このエラーを解決するには、ダブルクォーテーションをエスケープシーケンス(\”)で置き換える必要があります。

std::string message = "Hello, \"world\"!";

このように修正することで、コンパイルエラーが解消され、正しく実行できるようになります。

○不正なエスケープシーケンスの使用

C++には、予約されたエスケープシーケンスがあります。

例えば、\nは改行、\tはタブを表します。

しかし、存在しないエスケープシーケンスを使用すると、コンパイルエラーが発生します。

#include <iostream>
#include <string>

int main() {
    std::string message = "Hello, \world!";
    std::cout << message << std::endl;
    return 0;
}

このコードでは、\wという存在しないエスケープシーケンスを使用しています。

コンパイル時に次のようなエラーが発生します。

error: unknown escape sequence '\w'

不正なエスケープシーケンスを使用すると、コンパイラは文字列の解釈に失敗し、エラーを報告します。

この問題を解決するには、正しいエスケープシーケンスを使用するか、バックスラッシュ()自体をエスケープする必要があります。

std::string message = "Hello, \\world!";

上記のように、バックスラッシュを二重にすることで、バックスラッシュ自体を文字列に含めることができます。

○文字列リテラルの終端忘れ

文字列リテラルを使用する際に、終端のダブルクォーテーション(“)を忘れてしまうことがあります。

#include <iostream>
#include <string>

int main() {
    std::string message = "Hello, world!;
    std::cout << message << std::endl;
    return 0;
}

このコードでは、文字列リテラルの終端であるダブルクォーテーションが欠けています。

コンパイル時に以下のようなエラーが発生します。

error: missing terminating " character

このエラーを解決するには、文字列リテラルの末尾にダブルクォーテーションを追加する必要があります。

std::string message = "Hello, world!";

このように修正することで、コンパイルエラーが解消され、正しく実行できるようになります。

●特殊文字を扱うための応用テクニック

ここまで、C++での特殊文字の扱い方について詳しく見てきました。

エスケープシーケンスや生文字列リテラルを使いこなせるようになったら、次はさらに応用的なテクニックに挑戦してみましょう。

特殊文字を適切に処理することは、プログラムの安全性と可読性を高めるために欠かせません。

ここからは、実際の開発現場でよく使われる特殊文字の扱い方をいくつか紹介します。

○サンプルコード7:ユーザー入力からの特殊文字の除去

ユーザーからの入力を受け取る際、特殊文字が含まれていると予期しない動作を引き起こす可能性があります。

そこで、入力された文字列から特殊文字を除去することが重要です。

#include <iostream>
#include <string>
#include <algorithm>

std::string removeSpecialCharacters(const std::string& input) {
    std::string result = input;
    result.erase(std::remove_if(result.begin(), result.end(), [](char c) {
        return !std::isalnum(c) && !std::isspace(c);
    }), result.end());
    return result;
}

int main() {
    std::string userInput;
    std::cout << "Enter a string: ";
    std::getline(std::cin, userInput);

    std::string sanitizedInput = removeSpecialCharacters(userInput);
    std::cout << "Sanitized string: " << sanitizedInput << std::endl;

    return 0;
}

このコードでは、removeSpecialCharacters関数を使って、ユーザー入力から特殊文字を除去しています。

この関数は、アルファベットと数字とスペース以外の文字を削除します。

実行結果

Enter a string: Hello, world! 123 @#$%
Sanitized string: Hello world 123

入力文字列から特殊文字(@#$%)が除去され、アルファベットと数字とスペースのみが残っていることがわかります。

○サンプルコード8:特殊文字を含む文字列の分割

文字列を特定の区切り文字で分割する際、区切り文字自体がエスケープシーケンスを含んでいる場合があります。

そのような場合でも正しく分割できるようにするには、エスケープシーケンスを考慮する必要があります。

#include <iostream>
#include <string>
#include <vector>
#include <sstream>

std::vector<std::string> splitString(const std::string& input, const std::string& delimiter) {
    std::vector<std::string> result;
    std::string escapedDelimiter = delimiter;
    std::string::size_type pos = 0;
    std::string::size_type prev = 0;

    while ((pos = input.find(escapedDelimiter, prev)) != std::string::npos) {
        result.push_back(input.substr(prev, pos - prev));
        prev = pos + escapedDelimiter.length();
    }

    result.push_back(input.substr(prev));
    return result;
}

int main() {
    std::string inputString = "apple\\,banana\\,cherry\\,date";
    std::string delimiter = "\\,";

    std::vector<std::string> tokens = splitString(inputString, delimiter);

    std::cout << "Original string: " << inputString << std::endl;
    std::cout << "Delimiter: " << delimiter << std::endl;
    std::cout << "Tokens:" << std::endl;

    for (const std::string& token : tokens) {
        std::cout << token << std::endl;
    }

    return 0;
}

このコードでは、splitString関数を使って、エスケープシーケンスを含む区切り文字で文字列を分割しています。

区切り文字自体がバックスラッシュを含む場合でも、正しく分割できるようになっています。

実行結果

Original string: apple\,banana\,cherry\,date
Delimiter: \,
Tokens:
apple
banana
cherry
date

バックスラッシュでエスケープされたカンマ(\,)を区切り文字として、文字列が正しく分割されていることがわかります。

○サンプルコード9:正規表現での特殊文字のエスケープ

正規表現を使って文字列を検索・置換する際、特殊文字をエスケープする必要があります。

正規表現では、特定の文字が特別な意味を持つため、それらの文字を検索パターンに含める場合はエスケープが必要です。

#include <iostream>
#include <string>
#include <regex>

std::string escapeRegexSpecialCharacters(const std::string& input) {
    std::string specialCharacters = R"([\^\.$|?*+(){}])";
    std::regex specialCharactersRegex(specialCharacters);
    return std::regex_replace(input, specialCharactersRegex, R"(\$&)");
}

int main() {
    std::string inputString = "Hello, world! (123) [456] {789}.";
    std::string escapedString = escapeRegexSpecialCharacters(inputString);

    std::cout << "Original string: " << inputString << std::endl;
    std::cout << "Escaped string: " << escapedString << std::endl;

    return 0;
}

このコードでは、escapeRegexSpecialCharacters関数を使って、正規表現で使用される特殊文字をエスケープしています。

エスケープされた文字列は、正規表現のパターンとして安全に使用できます。

実行結果

Original string: Hello, world! (123) [456] {789}.
Escaped string: Hello, world! \(123\) \[456\] \{789\}\.

正規表現で特別な意味を持つ文字(括弧、角括弧、中括弧、ドット)がバックスラッシュでエスケープされていることがわかります。

○サンプルコード10:特殊文字の出力制御

特殊文字を含む文字列を出力する際、意図しない動作を引き起こさないように注意が必要です。

特に、HTMLやXMLなどのマークアップ言語での出力では、特殊文字をエスケープする必要があります。

#include <iostream>
#include <string>
#include <unordered_map>

std::string escapeHtmlSpecialCharacters(const std::string& input) {
    std::unordered_map<char, std::string> escapeMap = {
        {'&', "&amp;"},
        {'<', "&lt;"},
        {'>', "&gt;"},
        {'"', "&quot;"},
        {'\'', "&#39;"}
    };

    std::string result = "";
    for (char c : input) {
        if (escapeMap.count(c) > 0) {
            result += escapeMap[c];
        } else {
            result += c;
        }
    }
    return result;
}

int main() {
    std::string htmlString = "<p>Hello, \"world\"! It's a beautiful day.</p>";
    std::string escapedString = escapeHtmlSpecialCharacters(htmlString);

    std::cout << "Original string: " << htmlString << std::endl;
    std::cout << "Escaped string: " << escapedString << std::endl;

    return 0;
}

このコードでは、escapeHtmlSpecialCharacters関数を使って、HTMLの特殊文字をエスケープしています。

&、<、>、”、’などの特殊文字は、それぞれ対応するHTMLエンティティに置き換えられます。

実行結果

Original string: <p>Hello, "world"! It's a beautiful day.</p>
Escaped string: &lt;p&gt;Hello, &quot;world&quot;! It&#39;s a beautiful day.&lt;/p&gt;

HTMLの特殊文字がエスケープされ、安全にHTMLとして出力できるようになっていることがわかります。

まとめ

C++での特殊文字の扱い方について、エスケープシーケンスや生文字列リテラルを中心に詳しく解説してきました。

特殊文字を適切に処理することは、C++プログラマーにとって欠かせないスキルの一つです。

C++における特殊文字の扱い方を習得することは、プログラミングスキルの向上につながります。

エスケープシーケンスや生文字列リテラルを使いこなし、特殊文字を適切に処理できるようになれば、バグのないコードを書けるようになるでしょう。