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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

5437閲覧

C++ wstringのreplace()が上手く置換できない

shinyaYS

総合スコア37

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2016/06/27 08:40

visual studio 2015 C++で以下の関数を作成しました。

wstring Replace(wstring String1, wstring String2, wstring String3)
{
wstring::size_type Pos(String1.find(String2));

while (Pos != std::string::npos) { String1.replace(Pos, String2.size(), String3); Pos = String1.find(String2, Pos + String3.size()); } return String1;

}

この関数の
String1.replace(Pos, String2.size(), String3);
の一文が上手く動作してくれません。

デバッガで確認すると、この一文の実行前は、
String1 = L" 見積ID int(11) DEFAULT NULL,\n"
String2 = L"見積ID"
String3 = L"estimateID"
となっているのですが、この一文を実行すると、
String1 = L" estimateID"
となってしまいます。

どこが、おかしいのでしょうか。

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

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

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

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

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

PineMatsu

2016/06/27 09:07

細かいことですが、タグは「C++」ですよね。 また、コードは```で囲うと見やすくなります。ブラウザ上では</>ボタンを押せばブロックを表示してくれます。
guest

回答2

0

質問内容に書いてない部分で問題がありそうですね。
ほかにReplace関数があったりしませんか?

呼び出し側のコードも含めて書いてみてください。
その際に、’’’で囲むと変に文字が消えたりしないと思います。

C4717は再起のエラーですね。
wstring String4 = Replace(String1, String2, String3);の部分をReplaceの中に書いているとか。

Visual Studio Community 2015で問題なく置換されるので、コンパイラは気にしなくて大丈夫です。

C++

1#include <string> 2using namespace std; 3 4wstring Replace(wstring String1, wstring String2, wstring String3) 5{ 6 wstring::size_type Pos(String1.find(String2)); 7 8 while (Pos != string::npos) 9 { 10 String1.replace(Pos, String2.size(), String3); 11 Pos = String1.find(String2, Pos + String3.size()); 12 } 13 14 return String1; 15} 16 17 18int main() 19{ 20 wstring String1 = L" '見積ID' int(11) DEFAULT NULL,\n"; 21 wstring String2 = L"'見積ID'"; 22 wstring String3 = L"'estimateID'"; 23 wstring String4 = Replace(String1, String2, String3); 24 25 return 0; 26}

投稿2016/06/28 04:50

kopio

総合スコア487

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

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

shinyaYS

2016/06/28 07:38

確かに上記コードだけだと、上手くいきますね。 日本語は、UTF-8なので、ソースファイルをUTF-8にしてみましたが、 上手くいきました。 何故、私が作成したプログラムが上手くいかないのか、全く分かりません。 一度、ソース全体を書かせて下さい。 #include "stdafx.h" #include <string> #include <sys/types.h> #include <sys/stat.h> #include <direct.h> #include <fstream> #include <iostream> #include <vector> #include <sstream> using namespace std; // 定義ファイルの構造 typedef struct _DefineData { wstring beforeData; wstring afterData; } DefineData; void GetInputFile(string & inputFile, string & fileName, string & folderName) { int nIndex; nIndex = inputFile.rfind("\\"); // ファイル名を取得する fileName = inputFile.substr(nIndex + 1); // フォルダ名を取得する folderName = inputFile.substr(0, nIndex); } vector<wstring> split(const wstring &str, char sep) { vector<wstring> v; int nIndex = str.find(sep); wstring before = str.substr(0, nIndex); wstring after = str.substr(nIndex + 1); v.push_back(before); v.push_back(after); return v; } int ReadDefineFile(vector<DefineData> & vDefine) { FILE * fp; wstring str; TCHAR data[128]; const errno_t er = fopen_s(&fp, "define.txt", "rt,ccs=utf-8"); while (_fgetts(data, 128, fp) != NULL) { str = data; int nIndex = str.length(); str[nIndex - 1] = NULL; vector<wstring> vData; vData = split(str, '/'); DefineData stDefine; stDefine.beforeData = vData[0]; stDefine.afterData = vData[1]; vDefine.push_back(stDefine); } return 0; } // wstring Replace(wstring String1, wstring String2, wstring String3) { wstring::size_type Pos(String1.find(String2)); while (Pos != std::string::npos) { String1.replace(Pos, String2.size(), String3); Pos = String1.find(String2, Pos + String3.size()); } return String1; } int ReplaceData(string inputFile, vector<DefineData> vDefine, string outputFile) { FILE* fpr = NULL; FILE * fpw = NULL; TCHAR data[128]; wstring str; const errno_t er = fopen_s(&fpr, inputFile.c_str(), "rt,ccs=utf-8"); const errno_t er1 = fopen_s(&fpw, outputFile.c_str(), "w,ccs=utf-8"); while (_fgetts(data, 128, fpr) != NULL) { str = data; for (int i = 0; i < (int)vDefine.size(); ++i) { str = Replace(str, vDefine[i].beforeData, vDefine[i].afterData); } _fputts(str.c_str(), fpw); } fclose(fpr); fclose(fpw); return 0; } int main(int argc, char * argv[]) { int rtn; string inputFile = argv[1]; vector<DefineData> vDefine; setlocale(LC_ALL, ""); // 入力ファイルのフォルダとファイル名を取得する string fileName; string folderName; GetInputFile(inputFile, fileName, folderName); // 出力フォルダがなければ、作成する string outputFolder = folderName + "\\" + "(置換済)"; struct _stat stStat; if (_stat(outputFolder.c_str(), &stStat)) { _mkdir(outputFolder.c_str()); } string outputFile = outputFolder + "\\" + fileName; // 定義ファイルの読み込み rtn = ReadDefineFile(vDefine); if (rtn != 0) { exit(1); } // 入力ファイルの読み込みと出力ファイルの作成 rtn = ReplaceData(inputFile, vDefine, outputFile); if (rtn != 0) { exit(1); } return 0; }
kopio

2016/06/28 08:16

パッと見て思いつくのはfopen_sの引数で、 ccs=utf-8となっているのをccs=UTF-8と 大文字にしてみるぐらいでしょうか。
shinyaYS

2016/06/29 00:21

utf-8大文字にしてみましたが、変わりませんでした。 プログラムソースファイルの文字セットがSJISだったので、それをUTF-8に変わりませんでした。 プロジェクトプロパティの文字セットは"Unicodeを使用する"にしているのですが、これを"マルチバイト文字セット"を使用するに変える必要はあるでしょうか? (今、変えるとビルドでたくさんのエラーが発生してしまうのですが)
kopio

2016/06/30 00:46

文字コードが混乱していますね。 読み込むファイルはUTF-8という認識であっていますか? fopen_sのオプションは大文字小文字で解釈変わるので要注意です。 https://msdn.microsoft.com/ja-jp/library/z5hh6ee9.aspx 他に思いつく点としては、fopen_sを_tfopen_sに変えてみてらどうなりますか?
shinyaYS

2016/06/30 02:16

読み込むファイルはUTF-8です。 fopen_sを_tfopen_sに変えましたが、結果は変わりませんでした。 もうこのプログラムは諦めて、他の手段を検討しようかと思っています。
guest

0

ベストアンサー

こんにちは。

MSVC 2015 update 2にてやってみましたが、手元では正常に動作しました。

C++

1wstring String1 = L" 見積ID int(11) DEFAULT NULL,\n"; 2wstring String2 = L"見積ID"; 3wstring String3 = L"estimateID"; 4wstring String4 = Replace(String1, String2, String3);

デバッガで止めて、String4を確認したら、L" estimateID int(11) DEFAULT NULL,\n"となってます。

String1 = L" estimateID"

となってしまいます。

String2が実はL"見積ID\0"のようになってないでしょうか?


【追記】
私が動作検証したソースです。

C++

1#include <iostream> 2#include <string> 3 4using namespace std; 5 6wstring Replace(wstring String1, wstring String2, wstring String3) 7{ 8 wstring::size_type Pos(String1.find(String2)); 9 10 while (Pos != std::string::npos) 11 { 12 String1.replace(Pos, String2.size(), String3); 13 Pos = String1.find(String2, Pos + String3.size()); 14 } 15 16 return String1; 17} 18 19int main(int argc, char* argv[]) 20{ 21 wstring String1 = L" `見積ID` int(11) DEFAULT NULL,\n"; 22 wstring String2 = L"`見積ID`"; 23 wstring String3 = L"`estimateID`"; 24 wstring String4 = Replace(String1, String2, String3); 25 26 wcout << String4 << endl; 27 28 return 0; 29}

投稿2016/06/27 09:02

編集2016/06/28 10:18
Chironian

総合スコア23272

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

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

shinyaYS

2016/06/27 23:45

申し訳ありません。 少し、情報に誤りがありました。 実行前は String1 = L" `見積ID` int(11) DEFAULT NULL,\n" String2 = L"`見積ID`" String3 = L"`estimateID`" でした。 (値をコピーして、貼り付けた時に"`"の文字が落ちてしまったようです) それで、実行結果は String1 = L" `estimateID`" です。 以上。よろしくお願いします。
Chironian

2016/06/28 02:33

コピペしてやってみました。wcout << String4 << endl;にて下記が出力されました。 `estimateID` int(11) DEFAULT NULL, う~ん、不思議ですね。ビルド方法等に差があるのでしょうか? 私はプロジェクトの新規作成でVisual C++ → Win32 → Win32コンソール アプリケーションで生成し、Win32のDebugモードでビルドし、IDEから実行してます。 後、私のMSVCはupdate2を当ててます。
shinyaYS

2016/06/28 02:51

私は、Visual Studio Communityを1週間程前にインストールして、それを使用しています。 Communityだと何か差があるのでしょうか?
shinyaYS

2016/06/28 03:42

私も、 wstring String4 = Replace(String1, String2, String3); と変えてビルドした所、下記のわーにんぐが出ました。 warning C4717: 'Replace': すべてのコントロールのパス、関数を回帰するとランタイム スタック オーバーフローが発生します。 そして、実行すると、スタックオーバーフローのエラーが発生しました。 これは、どのように、回避されたのでしょうか?
Chironian

2016/06/28 10:19 編集

私が使っているのもVisual Studio 2015 ommunityです。 shinyaYSさんはwstring String4 = Replace(String1, String2, String3);をReplace()関数の中に入れたのではないでしょうか? そうすると再帰定義となり、かつ、終了条件の指定がないのでスタックをひたすら消費するので、スタックオーバーフローになります。 私の試しているソースを回答へアップします。
Chironian

2016/06/29 03:51 編集

kopioさんへの回答のソースを確認しました。 String3の最後に\0が入ってます。 デバッガでは一見入っていないように見えますが、更にポイントして1文字単位で表示させると分かります。 原因は、ReadDefineFile()で文字列の最後にNULL文字を入れているからですね。 std::stringはNULL終端を仮定しないです。文字数をきっちり管理しているので途中にNULL文字があっても管理されます。それをc_str()で取り出すとC言語処理系はNULL終端で解釈するため、意図と異なる動作になるのだと思います。Visual StrudioもC言語文字列として解釈し表示しているようです。 resize()すれば大丈夫な筈です。
shinyaYS

2016/06/30 03:51

ありがとうございました。 言われた通り、\0が入っていました。 resize()して正常に動作する事を確認しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問