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

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

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

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

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

Q&A

解決済

1回答

823閲覧

csvデータの取り出したいセルの値がなくなるまでセルの値(string型の文字列)をvector<string>に格納する方法

roatt

総合スコア45

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

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

0グッド

0クリップ

投稿2022/02/24 05:21

編集2022/02/25 06:02

前提・実現したいこと

csvを読み込み、特定の列の2行目以降のセルの値を、セルの値がなくなるまでvector<string>に格納したいです。
例)「値R」の数値をVector<string> csvData = {0.4, 0.4, 0.47, 0.404}のように格納

↓csvSearch.csv
イメージ説明

※csvのデータがさらに書き込まれた場合にもVector<string> csvDataが自動で書き込まれたセルの値を格納できるようにしたいため、「セルの値がなくなるまで」Vector<string> csvDataに格納したいです。
例)"値R"の数値が、「0.4, 0.4, 0.47, 0.404」→「0.4, 0.4, 0.47, 0.404, 0.3, 0.25, 0.78」と上書きされたとき、
Vector<string> csvData = {0.4, 0.4, 0.47, 0.404} → csvData = {0.4, 0.4, 0.47, 0.404, 0.3, 0.25, 0.78}にコードを書き直さなくても格納できる状態

発生している問題・エラーメッセージ

①セルの値をstring型でとっているため、以下「該当のソースコード」のように書くとvector<string>型とデータ型があっておらず、以下のエラーが出てしまいます。

これらのオペランドと一致する演算子 "=" はありません

該当のソースコード

cpp

1#include <iostream> 2#include <fstream> 3#include <map> 4#include <sstream> 5#include <string> 6#include <vector> 7 8using std::cout; using std::endl; 9using std::ifstream; using std::string; 10using std::getline; using std::istringstream; 11using std::vector; 12 13/** 14*csvファイルから指定された行の文字列を取得する 15*@param fin 16*@param col_row 行番号 17*@return csvファイルの指定された行の文字列 18*/ 19[[nodiscard]]string get_row(ifstream& fin, int row) { 20 try { 21 int cur_row = 1; //読み込んだ行数。先頭行は読み込み済みだから「1」を初期値に指定 22 string line; //ファイルの行を格納する変数 23 24 while (getline(fin, line)) { //ファイルを1行ずつ読み込む 25 if (cur_row == row) { //指定された行が見つかった 26 return line; //行の文字列を返す 27 } 28 ++cur_row; 29 } 30 throw "指定された行が見つかりませんでした"; //指定された行が見つからないときは例外を投げる 31 } 32 catch (char const* message) { 33 34 } 35} 36 37/** 38*csvファイルの行の文字列から指定された列のセルの値を取得する 39*@param line csvファイルの行の文字列 40*@param col 列番号 41*@return csvセルの値 42*/ 43[[nodiscard]] string get_cell(string line, int col) { 44 try { 45 string token; //分割したセルの値を格納する変数 46 int cur_col = 0; //処理中の列の列番号 47 istringstream stream(line); //行の文字列をカンマで分割 48 49 while (getline(stream, token, ',')) { 50 if (cur_col == col) { //指定されたセルが見つかった 51 return token; //セルの値を返す 52 } 53 ++cur_col; //次のセルへ 54 } 55 throw "指定されたセルが見つかりませんでした"; //指定されたセルが見つからないときは例外を投げる 56 } 57 catch (char const* message) { 58 59 } 60} 61 62/** 63*csvファイルから指定したセルの値を取得する 64*@param filename csvファイルのファイル名 65*@param row 行番号 66*@aram col_name 列名 67*/ 68[[nodiscard]] string get_data(int row) { 69 string filename("csvSearch_test.csv"); 70 ifstream fin(filename); //ファイルを開く 71 int col = 2; //"値R"の列番号 72 string line = get_row(fin, row); //csvファイルの指定された行の文字列を取得 73 return get_cell(line, col); //行の文字列からセルの値を取得 74} 75 76int main() 77{ 78 vector<string> csvData; 79 int i = 2; 80 int q = 0; 81 82 while (get_data(i) == "") { 83 csvData = get_data(i); 84 cout << csvData[i] << endl; //vector<string> csvDataに入ったか確認用 85 86 i++; 87 q++; 88 } 89}

↓int32_t様ご助力のコード

cpp

1#include <iostream> 2#include <fstream> 3#include <map> 4#include <sstream> 5#include <string> 6#include <vector> 7#include <optional> 8 9using std::cout; using std::endl; 10using std::ifstream; using std::string; 11using std::getline; using std::istringstream; 12using std::vector; 13using std::optional; 14using std::nullopt; 15 16/** 17*csvファイルの行の文字列から指定された列のセルの値を取得する 18*@param line csvファイルの行の文字列 19*@param col 列番号 20*@return csvセルの値 21*/ 22optional<string> get_cell(string line, int col) { 23 24 string token; //分割したセルの値を格納する変数 25 int cur_col = 0; //処理中の列の列番号 26 istringstream stream(line); //行の文字列をカンマで分割 27 28 while (getline(stream, token, ',')) { 29 if (cur_col == col) { //指定されたセルが見つかった 30 return token; //セルの値を返す 31 } 32 else { 33 } 34 ++cur_col; //次のセルへ 35 } 36 return nullopt; 37} 38 39/** 40*csvファイルから指定したセルの値を取得する 41*@param filename csvファイルのファイル名 42*@param row 行番号 43*@aram col_name 列名 44*/ 45[[nodiscard]] vector<string> get_data() { 46 vector<string> cellData; 47 string filename("C:/Users/AIM-CAE-PC12/Documents/Unreal Projects/Arduino_test/csvSearch_test.csv"); 48 ifstream fin(filename); //ファイルを開く 49 int col = 2; //列名に"値R"の列番号を取得 50 //string line = get_row(fin, row); //csvファイルの指定された行の文字列を取得 51 52 int cur_row = 1; //読み込んだ行数。先頭行は読み込み済みだから「1」を初期値に指定 53 string line; //ファイルの行を格納する変数 54 55 while (getline(fin, line)) { //ファイルを1行ずつ読み込む 56 57 if (optional<string> cell = get_cell(line, col)) { 58 cellData.push_back(*cell); 59 } 60 61 ++cur_row; 62 } 63 return cellData; //行の文字列からセルの値を取得 64} 65 66int main() 67{ 68 vector<string> csvData; 69 70 csvData = get_data(); 71 72 for (size_t z = 0; z < csvData.size(); ++z) { 73 cout << csvData[z] << endl; //値が入っているか確認用 74 } 75}

試したこと

①get_data()、get_cell()をstring<vector>に、get_cell()内のstring tokenもstring<vector>に変更し、データ型を合わせようとしましたが、get_cell()内のgetline(stream, token, ',')で以下の★エラー文と int main内のwhile (get_data(i) == "")で △エラー文がでてしまいます。
②string → vector<string>へ型変換できないかも調べましたが、発見することができませんでした。

①のエラー文

★オーバーロードされた関数 "getline" のインスタンスが引数リストと一致しません △これらのオペランドと一致する演算子 "==" はありません

補足情報(FW/ツールのバージョンなど)

Visual Studio 2022

打開策がないかインターネットで調べてみましたが、いまだ発見できずにいます。
正直なところ、プログラミングの知識・技術もまだまだ未熟ですので、丁寧なご説明も打いただけると大変ありがたいです。
何か不明点あればご質問ください。
お手数ですが、よろしくお願いいたします。

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

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

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

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

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

KoichiSugiyama

2022/02/24 06:23

vectorに要素を追加する方法は調べられましたか?扱う型によらずvectorの使い方の基本的な部分を理解するだけで解決する問題だと思います。どのような検索ワードで調べられて打開策がみつからなかったのか質問文に記載されていなかったのですが、検索ワード「C++ vector」で検索すると、大量にページがヒットすると思いますので、そちらで一度調べることをお勧めします。
episteme

2022/02/24 06:24 編集

何をしたいんだかわかりません。 get_data()はstringを返すのだから、それを vector<string> に代入できるわけがないし。
roatt

2022/02/24 10:35

KoichiSugiyama様 ご助言ありがとうございます。 早速調べましたところ、vectorではpush.back()で値を格納するのですね。 配列という先入観でc言語と同じように考えてしまっておりました。 ありがとうございます。 episteme様 コメントありがとうございました。
BeatStar

2022/02/24 12:52

> 配列という先入観でc言語と同じように考えてしまっておりました うーん? C言語の配列みたいに考えても、csvData[i] = get_data(i); のようになるはずですが… C++の std::vectorは動的配列( C言語でいうmalloc/freeでやるアレ )をクラスに包んだものですから、 アクセス方法が異なります。 一応C言語の配列と同じように a[i] = read(); とかみたいにやることは可能ですが、C言語でいうmallocに相当する処理が必要です。コンストラクタで サイズを指定するのです。
guest

回答1

0

ベストアンサー

どういう設計にしたいのかご自身でわからない状態なんだろうと思います。

私なら、元のコードをできるだけ残すとして以下のようにします。

  • get_row() はすべて削除
  • get_cell()std::optional<std::string> を返すように変更する。指定セルが見つからない場合はstd::nulloptを返す。
  • get_data() の戻り値は std::vector<std::string> にする。
  • get_data() の最初に std::vector<std::string> のローカル変数を宣言し、それを戻り値にする。
  • get_data() 内で get_row() でやっていたような getline() のループをする。得た行はそのまま get_cell() に渡して、その結果が std::nullopt でなければそれを std::vector<std::string> に追加する。
  • main() では csvDataget_data() の戻り値を代入する。ループの判定は csvData のサイズで行う

投稿2022/02/24 08:15

int32_t

総合スコア20872

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

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

roatt

2022/02/24 11:26 編集

ご助言ありがとうございます。 C++もプログラミングも初心者でネットのサンプルコードばかり見て今まで作成していたため、どのように設計するかなどもint32_t様の仰る通りよくわかっていませんでした。 今、int32_t様のご助言の通りにコードを作ってみたのですがエラーが9個出ています。 大変お手数ですが、以下のコードを見てご助言願えないでしょうか。 コメントアウトの★部分はエラーコードの内容です。1行に複数あるものもあったため、少し文は変えているところがあります。 また、以下コードを作成していく中でご助言を読んで不明点あったため質問させていただきます。 >get_data() 内で get_row() でやっていたような getline() のループをする。得た行はそのまま get_cell() に渡して、その結果が std::nullopt でなければそれを std::vector<std::string> に追加する。 得た行をそのままgetlineに渡すというコードはどうするのでしょうか? コード内では「string line = get_cell(line, col);」というように表記しておりますが、return文を活用するという理解であっていますか? しかし、1つの関数にreturn文は1つと聞いたことがあるため、そうすると return cellData;が返せないのではないかという懸念があります… >main() では csvData に get_data() の戻り値を代入する。ループの判定は csvData のサイズで行う sizeでの判定方法がいまいちわかっておりません。 文字のサイズが0じゃなければ、ループするという理解であっておりますでしょうか? それ以外にも仰っていただいたこととやっていることが全然違うということであればどんどん仰っていただけましたらうれしいです。 、、、 #include <iostream> #include <fstream> #include <map> #include <sstream> #include <string> #include <vector> #include <optional> using std::cout; using std::endl; using std::ifstream; using std::string; using std::getline; using std::istringstream; using std::vector; using std::optional; //名前空間"std"にメンバー"optional"がありません ★ using std::nullopt; //名前空間"std"にメンバー"nullopt"がありません ★ optional<string> get_cell(string line, int col) { //optionalはテンプレートではありません ★ try { optional<string> token; //分割したセルの値を格納する変数 //識別子"optional","token"が定義されていません stringの型名は使用できません ★ int cur_col = 0; //処理中の列の列番号 istringstream stream(line); //行の文字列をカンマで分割 while (getline(stream, token, ',')) { //オーバーロードされた関数"getline"のインスタンスが引数リストと一致しません ★ if (cur_col == col) { //指定されたセルが見つかった return token; //セルの値を返す } else { return nullopt; //識別子"nullopt"が定義されていません ★ } ++cur_col; //次のセルへ } throw "指定されたセルが見つかりませんでした"; //指定されたセルが見つからないときは例外を投げる } catch (char const* message) { } } /** *csvファイルから指定したセルの値を取得する *@param filename csvファイルのファイル名 *@param row 行番号 *@aram col_name 列名 */ [[nodiscard]] vector<string> get_data(int row) { vector<string> cellData; string filename("C:/Users/AIM-CAE-PC12/Documents/Unreal Projects/Arduino_test/csvSearch_test.csv"); ifstream fin(filename); //ファイルを開く int col = 2; //列名に"値R"の列番号を取得 //string line = get_row(fin, row); //csvファイルの指定された行の文字列を取得 int cur_row = 1; //読み込んだ行数。先頭行は読み込み済みだから「1」を初期値に指定 string line; //ファイルの行を格納する変数 while (getline(fin, line)) { //ファイルを1行ずつ読み込む if (cur_row == row) { //指定された行が見つかった string line = get_cell(line, col); //lineをget_cellに渡したい★ } ++cur_row; } return cellData; //行の文字列からセルの値を取得 } int main() { vector<string> csvData; int i = 2; while (csvData.size() != 0) { csvData = (get_data(i)); i++; } for (size_t z = 0; z < csvData.size(); ++z) { cout << csvData[z] << endl; //値が入っているか確認用 } } 、、、 長文になってしまい、申し訳ありません。
int32_t

2022/02/24 11:52 編集

コメントに長いコードを書くととても読みくいので、質問文に追記していただけますか? とりあえず、お使いのコンパイラで C++-11 以上を有効にしてください。clang や gcc だと -std=c++11 フラグです。 > 得た行をそのままgetlineに渡すというコード コメントに書かれたコードでだいたいできているようですよ。 ただ、変数 line が2重定義になってしまっているので、後者の変数名は変えましょう。 string line = get_cell(line, col); //lineをget_cellに渡したい★ → std::optional<std::string> cell = get_cell(line, col); > 文字のサイズが0じゃなければ、ループするという理解であっておりますでしょうか? 理解は間違ってますが、コードは合っている気がします。csvData は文字列ではなくstd::vector<>なので、セル数が0かどうか、という意味です。 あと、csvDataに代入してからループ判定する必要があるので、while(){} ではなく do { } while(); のほうが適切です。
roatt

2022/02/24 13:39

質問文へ追記しました。 コンパイラをC++17に変更したところ、optional,nullopt関係のものはエラー回避できました。 ありがとうございます。 しかし、getlineのところがどうしてもエラー回避できません… 「セル数が0かどうか」というと、そもそもget_data関数でvector<string>cellData = {0.4, 0.4, 0.47, 0.404}の状態になっているから、この配列があったらint mainのvector<string>csvDataに格納してねという命令ということでしょうか?
int32_t

2022/02/24 15:57 編集

> while (getline(stream, token, ',')) { //オーバーロードされた関数"getline"のインスタンスが引数リストと一致しません ★ 第2引数は std::string を渡す必要があるので、token の型は optional<string> ではなく string にしてください。stringからoptional<string>への変換は自動的に行われます。 > optional<string> cell = get_cell(line, col); cell に値が入っていたら、*cell を cellData に追加しましょう。 あと、get_data() に引数 row は不要ですよね。全列のセルを集めるので。 欲しい挙動を勘違いしてましたが、main()のループも不要ですね。vector<string> csvData = get_data(2); でよいです。
roatt

2022/02/25 01:43

>第2引数は std::string を渡す必要があるので、token の型は optional<string> ではなく string にしてください。stringからoptional<string>への変換は自動的に行われます。 エラー消えました。 >cell に値が入っていたら、*cell を cellData に追加しましょう。 if(optional<string> cell = get_cell(line, col)){ cellData.push_back(*cell); } ということでしょうか? >あと、get_data() に引数 row は不要ですよね。全列のセルを集めるので。 欲しい挙動を勘違いしてましたが、main()のループも不要ですね。vector<string> csvData = get_data(2); でよいです。 申し訳ありません。int32_t様のプログラムの動作がイマイチわかってないので、とんちんかんなことを言っているかもしれませんが、最終的に集めたいセルは”値R”の数値を取得したい場合、C列の2行目、3行目、4行目……という形なのですが、なぜ前列のセルを集める形になるのでしょうか? また、get_data()に引数rowが不要とありますが、そうなるとvector<string> csvData = get_data();ということになりますでしょうか? そうなるとget_data()内のif(cur_row == row)も不要ということでしょうか。 全然わかっておらず大変申し訳ありません。 どのような挙動を想定して、最終的に値R”の数値を取得したい場合、C列の2行目、3行目、4行目……という形まで持っていこうとしているのかもご教示いただけないでしょうか…? ※ソースコードも更新いたしました。(get_data()の引数rowの部分はまだです。)
roatt

2022/02/25 01:53

補足ですが、get_data()内にあるint col = 4;と設定すれば"値D"の値が入っているE列の2行目、3行目、4行目……と値が入っている分までvector<string>csvDataに格納したいです。 質問文の画像を使っての例だと、vector<string>csvData = {0.2, 0.2, 0.201, 0.255}が入ります。 ご承知済みでしたら申し訳ありません…
int32_t

2022/02/25 02:35 編集

get_cell() は指定列があればその文字列、なければstd::nulloptが返る想定です。try-throw-catch は不要です。 > そうなるとvector<string> csvData = get_data();ということになりますでしょうか? > そうなるとget_data()内のif(cur_row == row)も不要ということでしょうか。 はい。 はい。 get_data() は常に2行目以降から最後の行までの特定の列を集めるので、行を引数で指定する必要はないですよね。 rowが引数でなくても1行目を無視する処理を書けます。
roatt

2022/02/25 05:11

修正しました。 ビルドでき、デバッグエラーもなかったですが、コマンドプロンプトには何の数値も吐き出されていない状態です。 デバッグのステップインにて確認したところ、get_cell()内の while (getline(stream, token, ',')) { if (cur_col == col) { //指定されたセルが見つかった return token; //セルの値を返す } else { return nullopt; } ++cur_col; //次のセルへ } のところで1周目の1行目の文字列「日付,値H,値R,値J,値D」を読み込んだ時に、 if (cur_col == col)でnulloptを吐き出した後、++cur_col;で次のセルの値を判断する動作ができておりません。 きっとreturnでnulloptを返したため、get_cell()を抜け出したのかと思われます。 何かうまい書き方をご存じでしょうか? >rowが引数でなくても1行目を無視する処理を書けます。 なるほど、そういう手法があるのですね。 早とちりしてしまい申し訳ありません。
int32_t

2022/02/25 05:24

cur_col が col と等しくなるまでループを繰り返したいのですから、ループ内で return nullopt してはダメですよね。ループを抜けてもうセルがないときだけ return nullopt します。
roatt

2022/02/25 06:10

ありがとうございます。 これで、get_data()内のint col = 2で列設定をすることで、1行目から最後の行までvectorに格納するところまではできたことが確認できました。 >rowが引数でなくても1行目を無視する処理を書けます。 というところですが、同じくget_data()内のint cur_row = 1;を2にすると1行目が無視されるのかと思い、実行してみましたが、特に変わりなく「値R」がvector<string>へ格納されていました。 何か特別な関数等を使用するのでしょうか…?
int32_t

2022/02/25 06:21

get_data() 内で while ループの前に getline() を置いて、1行取得して取得した行に何もしなければいいです。 または while ループの後で std::vector のメソッドで最初の項目を消します。
roatt

2022/02/25 06:40

できました!!!!! たしかに使わない列は読み込ませて何もしなければいいですね… 完全に盲点でした… ここまでお付き合いいただき、何から何まで本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問