- ベストアンサー
c++のstd::stringについて
- VC++2008でフォームアプリケーションを作成しています。シリアルポートから受け取った文字列の一部を抜き出して処理をするため、std::stringを使用しようとしています。
- しかし、std::stringの一部を変数として使用するとビルドエラーが発生します。VBのmid相当の機能を実現するにはどうすればよいでしょうか?
- この問題を解決するためには、ポインタの理解が必要な場合があります。適切なポインタの使用方法を学ぶことで、VBのmidに相当する機能を実現できるでしょう。
- みんなの回答 (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回行う、などとしてみてください。 どんな言語を使っても、積極的に探究しようという姿勢があれば、すらすら使えるようになるのは時間の問題だと思います。
その他の回答 (4)
- LongSecret
- ベストアンサー率68% (22/32)
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の方法以外にも色々とあるようですから、興味がおありでしたら調べてみてください。
お礼
No3の方法は私の知識では理解できませんでした。(ネイティブとマネージド?の違いなどが良く分かっておりませんので,また検索などして勉強致します。) No4の回答の通りにしたところ文字列から任意の文字を取りだすことができました。ありがとうございます。 さらに,取りだした文字列(例えば000F)を数値に変換することは可能でしょうか? ここで質問してもよいのか迷いましたが,なにとぞご教授お願い致します。
- LongSecret
- ベストアンサー率68% (22/32)
こんばんは。 これは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)
「ビルドエラーになってしまいます」とのことですが, 具体的にはどこでどのようなエラーが出ているのですか?
お礼
デリゲードの中に std::string str(recieveddata,1,2);と書くと, 'System::String ^' から 'const std::basic_string<_Elem,_Traits,_Ax>' へは変換できません。 とのエラーが出てしまいます。
- f272
- ベストアンサー率46% (8576/18354)
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> バージョンがちょっと違うけど,エラーになんかならないよ。
お礼
ご回答ありがとうございます。 私の質問の仕方が悪かったかもしれません。 シリアルポートから受け取ったデータを,下記の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++を始めたばかりで,言葉の使い方などおかしい箇所があるかもしれませんんが,なにとぞアドバイスよろしくお願い致します。
お礼
お礼が遅くなってしまい申し訳ありませんでした。 お教えいただきた方法で必要な機能を実現することが出来ました。 LongSecret様のお陰で,プログラムが完成に近づいてきました。 本当のありがとうございました。