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回行う、などとしてみてください。
どんな言語を使っても、積極的に探究しようという姿勢があれば、すらすら使えるようになるのは時間の問題だと思います。
お礼が遅くなってしまい申し訳ありませんでした。
お教えいただきた方法で必要な機能を実現することが出来ました。
LongSecret様のお陰で,プログラムが完成に近づいてきました。
本当のありがとうございました。
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の方法以外にも色々とあるようですから、興味がおありでしたら調べてみてください。
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 );
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>
バージョンがちょっと違うけど,エラーになんかならないよ。
ご回答ありがとうございます。
私の質問の仕方が悪かったかもしれません。
シリアルポートから受け取ったデータを,下記の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が見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# このプログラミング誰か教えてくれませんか 2 2022/05/14 09:45
- C言語・C++・C# このプログラミング誰か教えてくれませんか 3 2022/05/13 17:27
- C言語・C++・C# このプログラミング誰か教えてください。 2 2022/04/22 18:48
- C言語・C++・C# このプログラミング誰か教えてください 9 2022/04/22 18:50
- C言語・C++・C# C++初心者です stirng 2 2022/09/20 20:43
- Visual Basic(VBA) 集めたシートのシート名を変更したい。 下記のコードでサブフォルダにあるファイルのSheet3を集めて 6 2022/08/23 10:38
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
- C言語・C++・C# C++プログラミングコードにポリモーフィズムを取り入れ方を教えてください。 2 2023/06/09 11:17
- Excel(エクセル) Excelにて、フォルダ内のTextファイルをマクロで統合すると文字化けしてしまう時の解消コード 4 2023/01/01 07:32
- Visual Basic(VBA) サブフォルダ(データ)にある複数の.xlsxファイルのSheet3のA2セルの値で01から左側をB2 2 2022/08/14 15:46
関連するカテゴリからQ&Aを探す
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
C言語のintとcharの違いってな...
-
2次元配列の文字"列"の初期化方法
-
エクセルのMID関数は、C言語では?
-
CStringからchar*への型変換に...
-
1Byte→Hex文字列変換がうまく行...
-
C++Builder 2009 テキスト...
-
C言語 strstrの実装
-
char** buffer (このような2...
-
C言語ポインタ 配列について
-
文字列を入力・表示
-
文字列置換のアルゴリズムを教...
-
C言語
-
文字列の連結
-
文字列の処理
-
char *str; と char* str;
-
DWORDとcharの変換
-
int型の文字列について
-
void型へのポインタ
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語のintとcharの違いってな...
-
char*を初期化したいのですが
-
C言語にて構造体のメンバがNULL...
-
CStringからchar*への型変換に...
-
strcat関数を自作したいです
-
new charとnew char[N]の違いは?
-
csvファイルをfscanfで読み込む...
-
char型にint型の数値を代入する。
-
動的メモリの初期化方法について。
-
C言語で文字列をかえす正しい書...
-
char 文字列型 の表現範囲が-12...
-
文字列str内の全ての数字を...
-
DWORDとcharの変換
-
fstream型オブジェクトを関数の...
-
C言語のプログラムについてです
-
小数点入りの文字列をfloat型に...
-
szとlpszの違い
-
const char* s1とただのchar s1...
-
文字列内の数字削除
-
c言語でポインタ変数を用いた配...
おすすめ情報