🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

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

Q&A

解決済

2回答

4332閲覧

C++で文字列から符号付き数値抽出

T.H.1234

総合スコア7

C++

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

0グッド

0クリップ

投稿2020/01/03 14:34

編集2020/01/04 16:10

C++でstring型の変数(char*型に対してでもcharの配列に対してでもありません。)に含まれる符号付き数値を抽出したいのですが、どのようなソースコードを書けば実現できるのかわかりません。数値については整数の場合と実数の場合の二つを考えています。
例えば string str="1233,2912 -9 0 -8.2" という文字列があったとき、
整数を抜き出す場合なら数値を格納する配列hに対してh[]=[1233,2912,-9,0,-8,2]と数字を格納し、
実数の場合ならピリオドによって整数部と少数部を区切るとして、h[]=[1233.0,2912.0,-9.0,0.0,-8.2]と数値を格納したいです。

色々調べてみた結果、符号付きでなく、かつ整数の場合なら以下のソースコードで実装できました。

C++

1#include <vector> 2#include <string> 3#include <iostream> 4#include <string.h> 5using namespace std; 6 7#define REP(i, n) for(int i = 0;i < n;i++) 8#define REPR(i, n) for(int i = n;i >= 0;i--) 9#define FOR(i, m, n) for(int i = m;i < n;i++) 10 11 12int main(){ 13 string s="44iiyo2"; 14 vector<int> vec; 15 long v; 16 char *a=new char[s.length()](); 17 char *b; 18 strncpy(a,s.c_str(),s.length()); 19 while(*a){ 20 if(isdigit(*a)){ 21 v=strtol(a,&b,10); 22 vec.push_back((int)v); 23 a=b; 24 }else { 25 a++; 26 } 27 } 28 REP(i,vec.size())cout<<vec[i]<<endl;; 29 30} 31

しかし符号付きに関しては実装方法がわからないままですので、符号付きの場合のソースコードを知りたいです。整数の場合のみでも構いませんので、ご教授いただけると幸いです。

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

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

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

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

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

guest

回答2

0

ベストアンサー

色々調べてみた結果、符号付きでなく、かつ整数の場合なら以下のソースコードで実装できました。

できていません。コンパイルできるソースに書き直してください。

istringstream を使えばできます。

C++

1#include <iostream> // cout 2#include <string> // string 3#include <vector> // vector 4#include <sstream> // istringstream 5using namespace std; 6 7int main() 8{ 9 // string s="44iiyo-2"; 10 string s = "1233,2912 -9 0 -8.2"; 11 vector<int> vec; 12 13 istringstream iss(s); 14 for (;;) { 15 int v; iss >> v; 16 if (iss) 17 vec.push_back(v); 18 else { 19 if (iss.eof()) break; 20 iss.clear(); 21 iss.ignore(); 22 } 23 } 24 for (int i = 0; i < vec.size(); i++) 25 cout << vec[i] << endl; 26}

このコードが動いたからといって、「解決しました」だけで済まさず、
理解したかどうかをコメントしてください。

追記
int の代わりに double として読み込むなら、

C++

1#include <iostream> // cout 2#include <string> // string 3#include <vector> // vector 4#include <sstream> // istringstream 5#include <iomanip> // setprecision 6using namespace std; 7 8int main() 9{ 10 string s = "1233,2912 -9 0 -8.2"; 11 vector<double> vec; 12 13 istringstream iss(s); 14 for (double v; ; ) 15 if (iss >> v) vec.push_back(v); 16 else if (iss.eof()) break; 17 else iss.clear(), iss.ignore(); 18 19 cout << fixed << setprecision(1); 20 for (auto v : vec) cout << v << endl; 21}

 
追記2
コンパイルできるソースに修正されたようですが、まだ問題があります。

s は "44iiyo2" ですから、s.length() は 7 です。
char *a = new chars.length(); で 7バイトの領域が確保され、ゼロで初期化され、a はその領域を指します。
strncpy(a, s.c_str(), s.length()); で a の指す領域に 7バイトコピーされますが、文字列の終端を示す '\0' はコピーされません。
従って while (*a) でループが終了するかどうかは不定です。

s.c_str() が s の中の "44iiyo2" という '\0' で終端する文字列を返すので、
const char *a = s.c_str(); で十分です。

符号付で読み込みたいなら、isdigit だけでなく '-' や '+' もチェックすべきでしょう。

C++

1#include <vector> // vector 2#include <string> // string 3#include <iostream> // cout, endl 4#include <cstdlib> // strtol 5using namespace std; 6 7int main() 8{ 9 string s = "1233,2912 -9 0 -8.2"; 10 vector<int> vec; 11 const char *a = s.c_str(); 12 while (*a) { 13 if (isdigit(*a) || *a == '-' || *a == '+') { 14 char *b; 15 int v = (int) strtol(a, &b, 10); 16 if (b == a) // 符号の後に数字が来ない場合のエラーをチェック 17 a++; 18 else { 19 vec.push_back(v); 20 a = b; 21 } 22 } else 23 a++; 24 } 25 for (int i = 0; i < vec.size(); i++) cout << vec[i] << endl; 26}

int の代わりに double で読み込みたい場合は、
vevtor<double> vec; にして、double v = strtod(a, &b); とすればよいでしょう。

コメントにある質問への回答

②if(iss)によってissのfailbitが立っていないか確認。

failbit だけでなく、eofbit も badbit も立っていなくて
正常に数値を読み込んだことを確認しています。

①iss>>v という操作を行った後、issはどう変化しているのか?(読み取り位置が変化しているという認識でいいのか?)

正常に数値を読み込んだときは、読み取り位置が変化しますが、
数値を読み込めなかったときは、読み取り位置は変わらず、
failbit または eofbit が立ちます。

②issのfailbitが立った場合にbreakから抜け出してもこのプログラムは正常に動く気がするが、issに対しclearとignoreを施しているのはなぜか?(このプログラムを書き加えてissを再利用する場合に備えているため?)

eofbit が立った場合は、break で forループを抜けますが、
failbit が立った場合、ループは継続します。
clear で failbit をリセットしないと、次の iss >> v が実行できません。
ignore で読み取り位置を一つ進めないと、次の iss >> v で同じ結果になります。

③eof()は読み取り位置が終端まで来たかどうかを判定しているという認識でよいか。

そういう認識でよいでしょう。

投稿2020/01/03 15:33

編集2020/01/05 17:23
kazuma-s

総合スコア8224

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

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

T.H.1234

2020/01/05 05:53 編集

とても丁寧な回答ありがとうございます。 最初に投稿していただいたコードのistringstreamについて、自分が理解できているかを確認したいので、いくつかのサイトを調べた上での自分の認識をお伝えします。御手数おかけしますが、ストリームまわりの理解が不十分だと思いますので用語の誤りや文意が不明な点がありましたら指摘してくださると助かります。 ①iss>>v は入力ストリームissからvへの入力を行い、issは今まで読んだ部分よりも後ろの部分で初めてでてくる数字を読み込み、issはその数字まで読み込んだと読み取り位置(この読み取り位置に対してなにか用語が存在するなら知りたい)を変更しvに数字を入力する。またすでに読み込んだ部分よりも後に数字が存在しないなら、vには何も入力されずissのfailbitが立つ。 ②if(iss)によってissのfailbitが立っていないか確認。 ③iss.eof()==trueであれば文字列を終端まで読み切ったことになるのでbreakしforループから抜け出す。 ④iss.clear()はこのコードにおいてはissのfailbitをクリアする。 ⑤iss.ignore()はデフォルト引数が(1,Traits::eof())であり、第二引数がtraits::eof()の場合には特定の文字までは読み込むという操作をしなくなるため、ここでは必ず一文字分読み取り位置を進める。 上記の自分の理解の正誤問わずに尋ねたい部分 ①iss>>v という操作を行った後、issはどう変化しているのか?(読み取り位置が変化しているという認識でいいのか?) ②issのfailbitが立った場合にbreakから抜け出してもこのプログラムは正常に動く気がするが、issに対しclearとignoreを施しているのはなぜか?(このプログラムを書き加えてissを再利用する場合に備えているため?) 長くなってしまいましたが、ご返信いただけると幸いです。追記2のソースコードには今から目を通します。
T.H.1234

2020/01/05 05:51

上記の自分の理解の正誤問わずに尋ねたい部分の追加の質問 ③eof()は読み取り位置が終端まで来たかどうかを判定しているという認識でよいか。
T.H.1234

2020/01/06 07:36

長い質問に対して非常に丁寧な回答をしていただきありがとうございました。 stringstream周りの理解が一気に深まりました。 追記2に関しては自力で十分に理解できましたので尋ねることはございません。
guest

0

「C++ 文字列変換」でぐぐると変換法の解説出てきます

投稿2020/01/03 14:46

y_waiwai

総合スコア88038

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問