c++のstd::stringについて

このQ&Aのポイント
  • VC++2008でフォームアプリケーションを作成しています。シリアルポートから受け取った文字列の一部を抜き出して処理をするため、std::stringを使用しようとしています。
  • しかし、std::stringの一部を変数として使用するとビルドエラーが発生します。VBのmid相当の機能を実現するにはどうすればよいでしょうか?
  • この問題を解決するためには、ポインタの理解が必要な場合があります。適切なポインタの使用方法を学ぶことで、VBのmidに相当する機能を実現できるでしょう。
回答を見る
  • ベストアンサー

c++のstd::stringについて

VC++2008でフォームアプリケーションを作成しています。 シリアルポートから受け取った文字列の一部を抜き出して処理をするため,VBではmid関数に相当するような機能として,std::stringを使用しようとしています。 しかし, std::string str("ABC" ,1,2); とした場合は『BC』が問題なく返ってきましたが, std::string str(recieveddata ,1,2); のように,文字列の部分を変数にしたら,ビルドエラーになってしまいます。(ポインタ?を理解する必要があるのでしょうか?) どのようにすれば,VBのmid相当の機能を実現できるでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
回答No.5

どうも、お礼いただきました。 お勧めしない方法として書いたので間に合わせコードになっていますが No4の方サイドから攻めるなら、受け取りたい最大文字数が分かっていれば問題ないですが そうでない場合、正確に言うと受け取りに十分な長さを持つchar配列を「動的に確保し、解放」しないと、文字数不足となって意図した動作にならない可能性があります。 終端文字を含んだ、最大の格納文字数をlenとすると Cでは char* c = (char*)malloc( len ); //確保 free(c); //解放 C++では char* c = new char[ len ]; //確保 delete c; //解放 といった具合になります。 この最小限のlenをその都度求める方法は… String^は、.NETの文字列ということで .NETではUnicodeを使っているので さらに、char* はANSI… ということで、これもまた一筋縄ではいかない気がしますが 今回必要かどうか分からないので、どうにも必要そうだったらいってください。 .NETはUnicode固定とすれば char より wchar_t の文字列の方がすんなりいくかもしれません。 両対応したい場合はTCHARを採用するのが吉です。 Unicode文字列とANSI文字列の違いなどについてはこちら http://victreal.com/Junk/_T/index.html なお、関数内でのchar c[サイズ];の場合は、ローカル変数と言って、メモリ上ではなくスタック上に確保され、関数を抜けるときにスタックが巻き戻されるので、解放コードを書く必要はありません。(というより、書いてはいけない、が正しいです。) ただ、いずれの場合でも、lenやサイズを超えたところに書き込もうとした場合は、とても危険で、やってはいけない操作なので 場合によりますが、良心的なシステムならアプリが不正終了などするかもしれません。 動的に確保したら解放を明示的にしないといけない(あるいはすべき)というのは、ネイティブコードの基本原理なのですが、大規模コードだとこれを正確に行い続けるのは比較的難しくなります。 .NETサイド、つまりマネージド(こういったメモリ関連が自動的に管理される)なコードの利点は、それを意識する必要がないというところにあります。 String^ s = gcnew String("text"); の場合に解放処理を書く必要がないのは、使われなくなったら自動的に片づけ(ガベージコレクト)してくれたりするからです。 (というよりネイティブと違い、そう言う事を自分で完全には管理出来ないようになっています) それゆえ、内部的なチェックやメモリの配置換えなどが起こる可能性から、パフォーマンス的には、よく練られたネイティブコードが勝ることがほとんど、という感じなのです。 と、いうわけで >さらに,取りだした文字列(例えば000F)を数値に変換することは可能でしょうか? これは、つまり16進表記で000Fなどだった数字が 一端文字列 "000F" になったのをchar*やchar配列の文字列として受け取って その文字列から、また数値に戻す、といった事でしょうか? その場合は、'0'から'9'までは'0'をマイナスしてやることで同じ数値になるのですが 'a' から 'f' は規格上の保証はないです。 あとは一文字(1桁)ずれるごとに重みが16倍になると考えれば 例えばかなりCっぽく書くと unsigned int num = 0; char c_i; for ( int i=0; c[i]; ++i ){ num <<= 4; //や num *=16; c_i = tolower( c[i] ); //tolower関数などで大文字小文字をそろえつつ if ( '0' <= c_i && c_i <= '9' ){ num += ( c_i - '0'); continue; } switch ( c_i ){ case 'a': num += 10; break; case 'b': num += 11; break; case 'c': num += 12; break; case 'd': num += 13; break; case 'e': num += 14; break; case 'f': num += 15; break; } } //最終的にnumが数値 といった形になります。 例えば"A0C"→"0C"→12としたい、など最初の1文字をなくしたい場合は for 分の最初を int i = 0; → int i = 1; としたり、あるいはポインタ操作などが分かればそれこそいくらでも方法はあります。 "000F"→ 00 0Fとみなして 0, 15と直したい といったことであれば、ループの繰り返し条件の個所を、c[i]ではなくi<2などと回数固定にしたうえで 全体を2回行う、などとしてみてください。 どんな言語を使っても、積極的に探究しようという姿勢があれば、すらすら使えるようになるのは時間の問題だと思います。

con014057
質問者

お礼

お礼が遅くなってしまい申し訳ありませんでした。 お教えいただきた方法で必要な機能を実現することが出来ました。 LongSecret様のお陰で,プログラムが完成に近づいてきました。 本当のありがとうございました。

その他の回答 (4)

回答No.4

ecieveddata.assign( (char*)pin, barray->Length ); ↓ recieveddata.assign( (char*)pin, barray->Length ); でしたね。失礼しました。 どうやら、std::string以外では、charなどの配列の場合、こんなんでも動くみたいですね。 String^ s = "abc"; enum { SIZE = 255 }; char c[SIZE]; sprintf_s( c, SIZE, "%s", s ); //なんと!?…(笑) c[3] = '\0'; textRecieved->Text = gcnew String( c+1 ); ただ、printf系統の%sでSystem::String^がちゃんと保証されてるか調べてませんので、お勧めはできません。 保証されてたとしても、どちらかというと、.NETサイドに、ネイティブとマネージドコード間のマーシャリング用関数が色々と用意されていることから #3のようにちゃんとマーシャリング的な事をしてる方をオススメします。 マーシャリングについては、#3の方法以外にも色々とあるようですから、興味がおありでしたら調べてみてください。

con014057
質問者

お礼

No3の方法は私の知識では理解できませんでした。(ネイティブとマネージド?の違いなどが良く分かっておりませんので,また検索などして勉強致します。) No4の回答の通りにしたところ文字列から任意の文字を取りだすことができました。ありがとうございます。 さらに,取りだした文字列(例えば000F)を数値に変換することは可能でしょうか? ここで質問してもよいのか迷いましたが,なにとぞご教授お願い致します。

回答No.3

こんばんは。 これはC++/CLIのコードですね。(個人的には懐かしいです) C++/CLIはC++の拡張言語の一種で、C++を使う事も可能ですが、少なくとも System::String^ は ガベージコレクションなどが働く C++/CLI(マネージド)サイドの型で std::string は C++(ネイティブ)の型で これらの交換は、一筋縄ではいかないと思います。 詳しいことは長くなるので、↑の中で分からない単語があったら、それを検索などで調べてみてください。 説明のためにチェックとか省いてなるべく短くすると、イメージとしてはこんな感じでしょうか using System::Text::Encoding; Encoding^ encoding = Encoding::GetEncoding("shift_jis"); std::string recieveddata; array<Byte>^ barray = Encoding::Convert( Encoding::Unicode, encoding, Encoding::Unicode->GetBytes(recvData) ); pin_ptr<Byte> pin = &barray[0]; //ここでようやくString^ → std::string ecieveddata.assign( (char*)pin, barray->Length ); std::string str(recieveddata,1,2); //std::string → String^ textRecieved->Text = gcnew System::String( str.data(), 0, str.size(), encoding );

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

「ビルドエラーになってしまいます」とのことですが, 具体的にはどこでどのようなエラーが出ているのですか?

con014057
質問者

お礼

デリゲードの中に std::string str(recieveddata,1,2);と書くと, 'System::String ^' から 'const std::basic_string<_Elem,_Traits,_Ax>' へは変換できません。 とのエラーが出てしまいます。

  • f272
  • ベストアンサー率46% (7996/17095)
回答No.1

C:\test>type t.cpp #include<iostream> #include<string> int main() { std::string recieveddata("ABC"); std::string str(recieveddata,1,2); std::cout << str << std::endl; return 0; } C:\test>cl /EHsc t.cpp Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. t.cpp Microsoft (R) Incremental Linker Version 10.00.30319.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:t.exe t.obj C:\test>t BC C:\test> バージョンがちょっと違うけど,エラーになんかならないよ。

con014057
質問者

お礼

ご回答ありがとうございます。 私の質問の仕方が悪かったかもしれません。 シリアルポートから受け取ったデータを,下記のCOMRevieveBuffer に入れていき,改行コードが来たら,デリゲートに処理を渡し,その中で,必要なデータを抜き出す,というような処理を行いたいのです。 private: System::Void serialPort_DataReceived() { SerialDataRecievedDelegate^ dlgte = gcnew SerialDataRecievedDelegate( this, &SerialCommSample::Form1::SerialDataRecieved ); String^ RecievedData = serialPort->ReadExisting(); //改行コードを検出する if(RecievedData == "\r" ) { RecievedData = RecievedData + "\n"; COMRevieveBuffer = COMRevieveBuffer + RecievedData; this->Invoke(dlgte, COMRevieveBuffer); COMRevieveBuffer = ""; } } //別スレッドで起動される関数。 delegate void SerialDataRecievedDelegate(String^ recvData); private: void SerialDataRecieved(String^ recvData){ textRecieved->Text = recvData; } デリゲートの中で std::string recieveddata(recvData); std::string str(recieveddata,1,2); textRecieved->Text =str と書いてみましたが,ビルドエラーになってしまいます。 C++を始めたばかりで,言葉の使い方などおかしい箇所があるかもしれませんんが,なにとぞアドバイスよろしくお願い致します。

関連するQ&A

  • unsigned char SJis[2]からstd::stringに変換

    開発環境は VC++ 2008 Express Edition あるDLLの関数で戻り値としてShiftJISの1文字が格納された unsigned char SJis[2] が返され,これを呼び出し側のプログラムで使っている文字列 std::string str に順に追加していこうと思っています. そこで, unsigned char tmpSJis[3]; tmpSJis[0] = SJis[0]; tmpSJis[1] = SJis[1]; tmpSJis[2] = '\0'; str += std::string(tmpCode); というコードを書いてループさせたのですが, error C2440: '<function-style-cast>' : 'unsigned char *' から 'std::string' に変換できません。 というエラーが出てしまいうまく変換できません. これを解決する方法はありませんか?

  • std::stringクラスのc_str()で取得した文字列をいじることは可能ですか?

    c++で、以下のようなコードは問題ないでしょうか? // chrの中身の小文字を大文字にする void func(char* chr); std::string str("aaa"); const char* str_p = std.c_str(); func((char*)str_p); ←これは大丈夫ですか? //このあとstrに対して文字列を追加したりいろいろ処理する。 このようにc_strで取得したconst char*をconstをはずして 強引にいじくることは問題ないでしょうか?

  • STLのstringのサイズが0なのに、printfできちゃうのはなぜ

    STLのstringのサイズが0なのに、printfできちゃうのはなぜ? たとえば、以下のような小文字変換処理を考えます。 std::string str = "ABC"; std::string strTmp = ""; std::transform( str.begin(), str.end(), strTmp.begin(), ptr_fun(::tolower) ); この時、strTmpには"abc"が入っていると思っていたのですが、 strTmp.compare("abc") が0になりませんでした。strTmp.size()が0になっており、どうも空文字""と認識されているようです。 ですが、 printf( strTmp.c_str() ); とすると、"abc"と出力されます。printfで上記のようにデバッグしていたので、なかなかミスが発見できなかったのですが、strTmpの初期化で""を入れているために、compareがうまく動かなかったようです。 std::string strTmp = ""; のところを std::string strTmp = str ; と直すと、compareが期待通りに動きました。 長くなってすみませんが、ここからが質問です。 1.stringの初期化時に確保した文字列長は、その後変更できないのでしょうか?今まで、普通に""で初期化した後、別の文字列を代入したりしていたような気がするのですが…。 2.strTmpのサイズが0にもかかわらず、printfで(一見)期待通りの結果が出力されたのは、なぜでしょうか?printfは、ヌル文字までを出力する仕様みたいですが、そもそもstringのsize()も、ヌル文字までの長さを返すのではなかったでしょうか。 全ソースは、以下の通りです。 #include <string> #include <algorithm> #include <functional> #include <ctype.h> #include <stdio.h> int main() {  std::string str = "ABC";  std::string strTmp = ""; // strにすると動く  std::transform( str.begin(), str.end(), strTmp.begin(), std::ptr_fun(::tolower) );  printf( "compare = %d\n", strTmp.compare("abc") );  printf( "size = %d\n", (int)strTmp.size() );  printf( "strTmp = %s\n", strTmp.c_str() );  return 0; }

  • [VB.net] StringからByte配列への変換

    皆さんこんばんは。 最近VB.NET2000でVisual Basicをはじめたのですが、どうにもString型の値をByte配列へ変換するやり方が分かりません。 VB6.0だとByte配列にStringを代入するだけでいいそうなのですが、.NETで代入しようとすると『型"String"の値を"Byteの1次元配列"に変換できません』というビルドエラーが表れてしまいます。 CByte( Mid(str,i,1) ) CLng( Mid(str,i,1) ) 他にも上記コードのように一つずつ変換しようと試みたのですが、String中のある文字がワイドバイトらしく、System.InvalidCastExceptionが表れてしまいます。 ("『"を上下反転したような文字) ワイドバイトのStringを1バイトずつ区切ってByte配列やLong配列へ格納する手法というものはありませんでしょうか。 よろしくお願いします。

  • stringについて

    確認したいのですが、ある関数の引数はconst string& strでやる場合 文字列リテラルは、ヒープが確保してそれをポインタであるstringに渡される。だからコピーされる手間が減るとういことでよろしいのでしょうか? 環境はvisual C++ 2005です。

  • C/C++関数間でのStringクラスの扱い

    以下のようなコードを実行してみましたが思い通りに動いてくれません. "sample"という文字列がstrへとコピーされると思ったのですが. stringクラスのc_str()メソッドはconst char*だと言っているので無理矢理キャストしたのが原因でしょうか.stringクラスは記憶領域を自動で変更してくれるのではないのですか.それともこの挙動は仕様ですか. -------- 以下コード -------- #include <iostream> #include <string> using namespace std; int func(char *); int main(void) {     string str("");     func((char *)str.c_str());     cout << "String: " << str << endl;     return EXIT_SUCCESS; } int func(char *buf) {     buf = "sample";     return 0; } -------- 以上コード --------

  • boost::formatの値をstring型にコピーしたい

    boost::formatの値をstring型にコピーしたいのですが、うまくいきません。boost自体初めてで以下のサイトからダウンロードし、展開後VS2005のVCのインクルードフォルダーにboostフォルダーをまるまるコピーしただけですが・・・ http://sourceforge.net/project/showfiles.php?group_id=7586 boost 1.34.1 #include <iostream> #include <boost/format.hpp> using namespace std; using boost::format; void main(){ double x = 1.234; string str("abc"); //cout << format("%10.3f, [%16s]") % x % str << endl; // サンプルはこうでした。 // いったんstring型に入れて表示させたい。 string y; y = boost::format("%10.3f, [%16s]") % x % str; // エラー箇所 std::cout << y << std::endl; }

  • C++文字配列ソート関数を教えてください

    C++文字配列ソート関数を教えてください 配列に格納されている文字列をソートして完全一致するかを確認するロジックを作成したいのですが、上手くいきません。ネットなどでいろいろ調べたのですが>< 一応、以下に今の状態のソースを貼りました。 class StringSuzeLess { public; bool operator()( const std::string& Ihs, const std::string& rhs ) const { retrun ihs.size() < rhs.size(); } } int main() { // 文字列格納変数 chra str1[10][5+1]; char str2[10][5+1]; // 初期化 memset(str1, 0x00, sizeof(str1)); memset(str2, 0x00, sizeof(str2)); // 文字列設定 strcpy(str1[0], "AAAAA"); strcpy(str1[1], "BBBBB"); strcpy(str1[2], "CCCCC"); strcpy(str2[0], "CCCCC"); strcpy(str2[1], "BBBBB"); strcpy(str2[2], "AAAAA"); std::vector< std::string > vsmozi1; std::vector< std::string > vsmozi2; // ベクタに文字列設定 for(short LoopCnt = 0; LoopCnt < 3; LoopCnt++) { vsmozi1.push_back( std::string( str1[LoopCnt] ) ); } for(short LoopCnt = 0; LoopCnt < 3; LoopCnt++) { vsmozi2.push_back( std::string( str2[LoopCnt] ) ); } // ソート std::stable_sort( vsmozi1.begin(), vsmozi1.end(), StringSuzeLess()); std::stable_sort( vsmozi2.begin(), vsmozi2.end(), StringSuzeLess()); // ソート後の先頭から取得 for(short LoopCnt = 0; LoopCnt < 3; LoopCnt++) { // 不一致の場合 if(vsmozi1[LoopCnt] != vsmozi2[LoopCnt]) { return -1; } } return 0; }

  • VB.NET DLL 参照型String読込み

    VCで作成されたDLLの参照型Stringの読込みに困っております。 ご存じの方、お教え下さい。 VB6では次のような定義で、問題なく実行できます。 Private Declare Function VcToVb _ Lib "xxxxxxx.dll" Alias "VcToVb" _ (ByVal Input_DATA As String, ByVal Input_DATA_Cnt As Long, _ ByRef strData As String, ByRef Err_Cnt As Long, ByRef Err_DATA As String) As Long ------------------------------------ Out_Str , Err_Str の領域を確保 iRent = Check_VcToVb1(Input_STR, Input_Cnt, Out_STR, Err_Cnt, Err_STR) これを VB.NETで実施 Private Declare Ansi Function VcToVb _  Lib "xxxxxxx.dll" Alias "VcToVb" _ (ByVal Input_DATA As String, ByVal Input_DATA_Cnt As Int32, _ <MarshalAs(UnmanagedType.LPStr)> ByRef strData As StringBuilder, _ ByRef err_cnt As Int32, ByRef Err_DATA As StringBuilder) As Int32 (テストのため、strDataのみ) -------------------------------------- Out_Str , Err_Str の領域を確保 iRent = Check_VcToVb1(Input_STR, Input_Cnt, Out_STR, Err_Cnt, Err_STR) ******************************************* 調べた結果、VB.NETでは参照型は簡単にはいかないみたいで、StringBuilderを使用するとか、色々な方法を試しました。 結果は戻ってくるのですが、問題は DLLが文字列を返すときに、文字列内の項目区切文字として chr(0)を設定します。 このため、結果は Chr(0)の前までしか設定されません。 (VB.NETが Chr(0)を文字列の最後と判断する?) これを解決する方法はありますでしょうか。 現在は VB6で呼出す DLLを作成し、VB.NETはこれを使用していますが、 C#.Netならできるのでしょうか。 よろしくお願い申し上げます。

  • String str = (String)url

    classファイルでのアプレットで、自分のパスを編集しようと思っています。 自分のパスをgetCodeBase()で得て、文字列として認識させたいのだけど それができていません。 String str; str = getCodeBase().toString; コンパイルエラー シンボル: 変数 toString 位置 : java.net.URL の クラス strBuf = getCodeBase().toString; getCodeBase()はStringでなく、URL型を返すみたいです。 これを文字列のように編集したいからSrting型にしたいんだけど どうすればいいんですか?

    • ベストアンサー
    • Java

専門家に質問してみよう