質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
エスケープ処理

エスケープ処理とは、一連の文字や一文字に対して、一定の規則に従って別の意味を適用する処理過程です。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

6826閲覧

C++ の regex において、文字列としての(バックスラッシュ)を置換したい。

MomenToufu

総合スコア10

エスケープ処理

エスケープ処理とは、一連の文字や一文字に対して、一定の規則に従って別の意味を適用する処理過程です。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

置換

置換とは文字列中の特定の文字に対して、別の文字列に置き換えることを指します。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/08/18 10:46

編集2021/08/19 04:21

前提・実現したいこと

C++の std::regex において、任意の文字列に含まれる「文字としての(バックスラッシュ)」を置換することは可能でしょうか?
※任意の文字列はプログラム外から与える事を想定しているため、任意の文字列には「\」1文字が存在することがあります。

欲しい結果

プログラムの外部から文字列を指定し、その文字列に含まれる「\」を「\」に置換し出力したいと思っています。

例えば、コンソール上で次のようにすると、

text

1user> app.exe あい\うえお\かきくけこ

次の結果を得たいと思っています。

text

1あい\うえお\かきくけこ

試したこと

次の3つの方法で試しました。

  1. バックスラッシュをバックスラッシュでエスケープして実施(PatternA1)
  2. バックスラッシュをバックスラッシュでエスケープして実施(PatternA2)
  3. 生文字リテラルを用いて実施(PatternB)

また、対象とした文字列は次の3つです。
0. 表層
0. 予想予
0. あい\うえお\かきくけこ

以下にソースコードを示します。

C++

1#include <string> 2#include <regex> 3#include <iostream> 4 5void dispResultPatternA1(std::string inputStr) { 6 std::cout << "\t" << inputStr << " -> " << std::regex_replace(inputStr, std::regex("\"), "\\") << std::endl; 7} 8void dispResultPatternA2(std::string inputStr) { 9 std::cout << "\t" << inputStr << " -> " << std::regex_replace(inputStr, std::regex("\\"), "\\") << std::endl; 10} 11void dispResultPatternB(std::string inputStr) { 12 std::cout << "\t" << inputStr << " -> " << std::regex_replace(inputStr, std::regex(R"(\)"), "\\") << std::endl; 13} 14int main(int argc, char* argv[]) { 15 if (argc != 4) { return 1; } 16 17 std::string s1_Before = argv[1];//第一引数 ... あい\うえお\かきくけこ 18 std::string s2_Before = argv[2];//第二引数 ... 表層 19 std::string s3_Before = argv[3];//第三引数 ... 予想予 20 21 std::cout << " ● PatternA1" << std::endl; 22 try { 23 dispResultPatternA1(s1_Before); 24 dispResultPatternA1(s2_Before); 25 dispResultPatternA1(s3_Before); 26 } 27 catch (std::regex_error& e) { 28 std::cout << "\t" << e.what() << std::endl; 29 } 30 31 std::cout << " ● PatternA2" << std::endl; 32 try { 33 dispResultPatternA2(s1_Before); 34 dispResultPatternA2(s2_Before); 35 dispResultPatternA2(s3_Before); 36 } 37 catch (std::regex_error& e) { 38 std::cout << "\t" << e.what() << std::endl; 39 } 40 41 std::cout << " ● PatternB" << std::endl; 42 try { 43 dispResultPatternB(s1_Before); 44 dispResultPatternB(s2_Before); 45 dispResultPatternB(s3_Before); 46 } 47 catch (std::regex_error& e) { 48 std::cout << "\t" << e.what() << std::endl; 49 } 50 51 getchar(); 52 return 0; 53}

その結果次の様になりました。
そもそもPatternAでうまくいくと思ってましたがエラーが出てしまいます。そこで、エスケープのエスケープで置換したらいいのでは?と思いPatternA2で試してみました。すると、第一引数の文字列は欲しい結果となったのですが、他の特定の文字(ここでは、「表」「予」)も置換の対象となってしまいました。他に方法はないか調べていると、「生文字リテラル」なるものがあり、それで試してみると(PatternB)、PatternA2と同じ結果となりました。

text

1 ● PatternA1 2 regex_error(error_escape): The expression contained an invalid escaped character, or a trailing escape. 3 ● PatternA2 4 あい\うえお\かきくけこ -> あい\うえお\かきくけこ 5 表層 -> 表\層 6 予想予 -> 予\想予\ 7 ● PatternB 8 あい\うえお\かきくけこ -> あい\うえお\かきくけこ 9 表層 -> 表\層 10 予想予 -> 予\想予\

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/08/18 10:51

> コンソール上で次のようにすると、 コンソールの種類は何でしょうか?(状況によっては、コンソール自体がバックスラッシュで特殊な挙動をすることも考えられます。まず「きちんと渡せているか」も確認したほうがいいかもしれません)
MomenToufu

2021/08/19 00:58

コメントありがとうございます。 コンソールはWindowsのコマンドプロンプトです。問題なくデータが渡せているか確認したいと思います。
guest

回答3

0

ベストアンサー

書いてみました。

C++

1#include <iostream> 2#include <string> 3#include <cstdlib> // mbstowcs, wcstombs 4#include <regex> // wregex, 5#include <clocale> // setlocale 6using namespace std; 7 8wstring towstr(const string& s) 9{ 10 size_t n = s.size() + 1; 11 wchar_t *p = new wchar_t[n]; 12 mbstowcs(p, s.c_str(), n); 13 wstring ws(p); 14 delete[] p; 15 return ws; 16} 17 18string tostr(const wstring& ws) 19{ 20 size_t n = ws.size() * 3 + 1; 21 char *p = new char[n]; 22 wcstombs(p, ws.c_str(), n); 23 string s(p); 24 delete[] p; 25 return s; 26} 27 28string bs2(const string& s) 29{ 30 static wregex wr(L"\\"); 31 return tostr(regex_replace(towstr(s), wr, L"\\")); 32} 33 34int main() 35{ 36 setlocale(LC_CTYPE, ""); 37 38 string a[] = { "あい\うえお\かきくけこ", "表層", "予想予" }; 39 for (string& s : a) 40 cout << s << " -> " << bs2(s) << endl; 41}

エラー処理は省略しています。

実行結果

text

1あい\うえお\かきくけこ -> あい\うえお\かきくけこ 2表層 -> 表層 3予想予 -> 予想予

追記
マルチバイト文字が UTF-8 ではなく、Shift-JIS だと分かっていたとすれば、

C++

1#include <iostream> 2#include <string> 3#include <sstream> // ostringstream 4using namespace std; 5 6bool isleading(unsigned char c) { return (c ^ 0x20) - 0xa1 < 60u; } 7 8string bs2(const string& s) 9{ 10 ostringstream oss; 11 for (const char *p = s.c_str(); *p; p++) { 12 oss << *p; 13 if (*p == '\') oss << '\'; 14 else if (isleading(*p)) oss << *++p; 15 } 16 return oss.str(); 17} 18 19int main() 20{ 21 string a[] = { "あい\うえお\かきくけこ", "表層", "予想予" }; 22 for (string& s : a) 23 cout << s << " -> " << bs2(s) << endl; 24}

投稿2021/08/18 12:40

編集2021/08/18 16:19
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

MomenToufu

2021/08/19 02:02

回答ありがとうございます。 意図した動作が出来ました。 一部分かっていない箇所があるのですが、「追記」のコードの isleading は上位ビットから全角か半角か判断しているということで良いでしょうか?これに追加して、制御文字の判定も行っているのでしょうか?
kazuma-s

2021/08/19 09:26 編集

Shift-JIS は、マルチバイト文字で 1バイト文字と 2バイト文字が混在しています。 2バイト文字の先頭バイト(leading byte) は 0x81~0x9f と 0xe0~xfc です。 それを判別するのに、 c >= 0x81 && c <= 0x9f || c >= 0xe0 && c <= 0xfc と 書いてもいいのですが、(c ^ 0x20) - 0xa1 < 60u でも同じ判定ができます。 制御文字は無関係です。
MomenToufu

2021/08/19 10:08

回答ありがとうございます。ビット演算子(^)の事を知らなかったものでちょっと確認させていただきました。
guest

0

日本語の文字を扱うのならワイド文字系かマルチバイト文字系を使って処理するべきでしょうね。

投稿2021/08/18 11:15

itagagaki

総合スコア8402

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

MomenToufu

2021/08/19 01:02

回答ありがとうございます。表面上は今回の質問のようなコードを書いているのですが、実際にはShift-JISを前提としたその他コードがあるため、出来るだけShift-JISベースで処理させたいと思っています。
guest

0

他の特定の文字(ここでは、「表」「予」)も置換の対象となってしまいました。

シフトJISには、2バイト目に0x5c(バックスラッシュと同じコードポイント)が来る漢字があります。

なので、コードポイントだけ見て変換していると「表」や「予」の2バイト目にマッチしてしまいます。

投稿2021/08/18 10:53

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

MomenToufu

2021/08/19 01:00

回答ありがとうございます。「表」や「予」で置換の対象となってしまう理由について理解いたしました。Shift-JISはちょっと曲者ですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問