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

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

新規登録して質問してみよう
ただいま回答率
85.35%
ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

C++

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

Q&A

解決済

2回答

1671閲覧

大相撲の勝敗計算をし,成績の良い順にソートした結果のファイルを吐き出すプログラムを作成したい

wagashi_157

総合スコア51

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

ソート

複数のデータを、順序性に従って並べ替えること。 データ処理を行う際に頻繁に用いられ、多くのアルゴリズムが存在します。速度、容量、複雑さなどに違いがあり、高速性に特化したものにクイックソートがあります。

C++

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

0グッド

1クリップ

投稿2021/06/08 13:27

編集2021/06/10 04:36

前提・実現したいこと

大相撲の勝敗が書かれたファイルを読み込んで計算をし,成績の良い順にソートした結果のファイルを吐き出すプログラムを作成したい。

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

今,大相撲の勝敗が書かれたファイルを読み込み,勝敗を計算して成績の良い順にソートした結果のファイルを吐き出す(白星の数が同じならばそのうち黒星の数が少ない方を上に持って行く)プログラムを作成しているのですが,コンパイルできるのにも関わらず,結果を出力しようとしたらエラーが出てしまいました。以前ファイルの読み込みに関するご指摘をもとに修正したのですが,それでもSegmentation faultが表示されます。どこに原因があるのか分からないので,教えてください。特にファイルの読み込み方・クラスへの格納に自信がないので宜しくお願いします。

Segmentation fault

読み込むテキスト

######higashi.txt(一部表示)

Yokozuna Hakuhou Shiroboshi Shiroboshi Fusenpai Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Daieishou Takarafuji Ounoshou OOzeki Shoudai Kuroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi Shiroboshi Shiroboshi Kuroboshi Shiroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi Mitakeumi Ounoshou Takarafuji Hokutofuji Wakatakakage Meisei Shimanoumi Kiribayama Daieishou Myougiryu Takayasu Takanoshou Terunofuji Takakeishou Asanoyama ...... Maegashira_Jurokumaime Kaisei Kuroboshi Shiroboshi Shiroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi Shiroboshi Kuroboshi Shiroboshi Kuroboshi Shiroboshi Shiroboshi Kuroboshi Hidenoumi Akua Tsurugishou Chiyoshouma Daiamami Akiseyama Toyoyama Chiyonoou Kagayaki Houshouryu Terutsuyoshi Kotoekou Midorifuji Chiyotairyu Tobizaru

該当のソースコード

C++

1#include<iostream> 2#include<fstream> 3#include<vector> 4#include<iomanip> 5 6class Record{ //番付, 力士の名前, 成績, 対戦相手を格納するクラス 7 std::string rank, name; 8 std::vector<std::string> score, aite; 9 public: 10 /*コンストラクタを定義*/ 11 int shiro(){ 12 int shiro{0}; 13 /*白星または不戦勝の数を計算*/ 14 for (int i=0; i<score.size(); i++){ 15 if (score[i]=="Shiroboshi"||score[i]=="Fusenshou") 16 shiro++; 17 } 18 return shiro; 19 } 20 int kuro(){ 21 int kuro{0}; 22 /*黒星または不戦敗の数を計算*/ 23 for (int i=0; i<score.size(); i++){ 24 if (score[i]=="Kuroboshi"||score[i]=="Fusenpai") 25 kuro++; 26 } 27 return kuro; 28 } 29 int yasumi(){ 30 int yasumi{0}; 31 /*やすみの数を計算*/ 32 for (int i=0; i<score.size(); i++){ 33 if (score[i]=="yasumi") 34 yasumi++; 35 } 36 return yasumi; 37 } 38 39 std::string get_r(){/*rankに対するゲッター*/return rank;} 40 std::string get_n(){/*nameに対するゲッター*/return name;} 41 std::vector<std::string> get_s(){/*scoreに対するゲッター*/return score;} 42 std::vector<std::string> get_a(){/*aiteに対するゲッター*/return aite;} 43 void set_r(std::string r){/*rankに対するセッター*/rank=r;} 44 void set_n(std::string n){/*nameに対するセッター*/name=n;} 45 void set_s(std::vector<std::string> s){/*scoreに対するセッター*/score=s;} 46 void set_a(std::vector<std::string> a){/*aiteに対するセッター*/aite=a;} 47 48 void print(){ 49 std::cout<<"Banzuke:"<<rank<<std::endl; 50 std::cout<<"Shikona:"<<name<<std::endl; 51 std::cout<<"Seiseki:"; 52 for(auto e: score) 53 std::cout<<e<<" "; 54 std::cout<<std::endl; 55 std::cout<<"Taisen_aite:"; 56 for(auto e: aite) 57 std::cout<<e<<" "; 58 std::cout<<std::endl; 59 } 60}; 61 62class Table{ //Recordをvectorに格納するクラス 63 friend Record; 64 std::vector<Record> v; 65 public: 66 void add(Record r){ 67 /*vにrを追加*/ 68 for (size_t i=0; i<v.size(); i++){ 69 v[i].set_r(r.get_r()); 70 v[i].set_n(r.get_n()); 71 v[i].set_s(r.get_s()); 72 v[i].set_a(r.get_a()); 73 v.push_back(r); 74 } 75 } 76 void print(){ 77 for(auto e : v){ 78 e.print(); 79 } 80 } 81 void sort(){ 82 for(size_t i=0;i<v.size()-1;i++){ 83 size_t max{i}; 84 for(size_t j=i+1;j<v.size();j++) { 85 /*勝敗(白星,不戦勝,黒星,不戦敗)によってvをソートする(選択ソート) */ 86 if (v[j].shiro()>v[max].shiro()) max=j; 87 if (v[j].shiro()==v[max].shiro()) 88 if (v[j].kuro()<v[max].kuro()) max=j; 89 } 90 std::swap(v[i],v[max]); 91 } 92 } 93 94 void result(std::ofstream& fout){ 95 for(auto e : v){ 96 if(e.yasumi()>0) 97 fout<<std::setw(22)<<e.get_r()<<std::setw(20)<<e.get_n()<<std::setw(5)<<e.shiro()<<"Shou"<<std::setw(5)<<e.kuro()<<"Hai"<<std::setw(5)<<e.yasumi()<<"Kyu"<<std::endl; 98 else 99 fout<<std::setw(22)<<e.get_r()<<std::setw(20)<<e.get_n()<<std::setw(5)<<e.shiro()<<"Shou"<<std::setw(5)<<e.kuro()<<"Hai"<<std::endl; 100 } 101 } 102}; 103 104 105 106std::vector<std::string> split(std::string s, char sep=' ', char sep2='\t'){ 107 std::vector<std::string> v; 108 auto iter{s.begin()}; 109 while(iter!=s.end()){ 110 /*空白(sep=' ')またはタブ(sep2='\t')で区切られているsの要素をvに格納.string(iter_first,iter)でiter_firstとiterで指定された文字の一部抽出ができる*/ 111 auto last=iter; 112 while (last!=s.end()&&(*last!=sep||*last!=sep2)) 113 ++last; 114 v.push_back(std::string(iter,last)); 115 if (last!=s.end()) 116 ++last; 117 iter=last; 118 } 119 return v; 120} 121 122void import(std::ifstream& fin,Table& table){//力士ファイルの取り込み 123 Record record; 124 int n{0}; 125 std::string rank, name, score, aite, blank; 126 while(fin){ 127 if(n%5==0){ 128 /*finからrankを取り出しrecordに格納*/ 129 getline(fin,rank); 130 record.set_r(rank); 131 } 132 else if(n%5==1){ 133 /*finからnameを取り出しrecordに格納*/ 134 getline(fin,name); 135 record.set_n(name); 136 } 137 else if(n%5==2){ 138 /*finからscoreを取り出しrecordに格納. その際split関数を用いる*/ 139 getline(fin,score); 140 split(score,' ','\t').push_back(score); 141 record.set_s(split(score,' ','\t')); 142 } 143 else if(n%5==3){ 144 /*finからaiteを取り出しrecordに格納.その際split関数を用いる*/ 145 /*recordをtableに追加*/ 146 getline(fin,aite); 147 split(aite,' ','\t').push_back(aite); 148 record.set_a(split(aite,' ','\t')); 149 table.add(record); 150 } 151 else{ 152 getline(fin,blank); 153 } 154 n++; 155 } 156} 157 158int main(){ 159 std::ifstream fin("higashi.txt"); 160 std::ofstream fout("result.txt"); 161 162 Table table; 163 import(fin,table); 164 table.print(); 165 table.sort(); 166 table.result(fout); 167 return 0; 168}

試したこと

クラス型のvectorの表し方に問題がないか見直しました。選択ソートの際,size_t n=v.size()を利用せずに表しました。そして,ファイルの読み込みではそれぞれの行から出力されるようにvectorの値に注意しながらプログラムを作成しました。行単位でファイルを読み込む際にgetline()関数を利用してプログラムを作成しました。

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

C++17を使用しています。

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

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

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

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

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

BeatStar

2021/06/08 13:47

セグフォってことは『メモリ等のアクセス違反』かも。 たとえば、『vectorの要素数は1なのに、arr[2]とかにアクセスしているとか。 とりま、『vectorのsizeメンバ関数でサイズを調べて』みては?
BeatStar

2021/06/08 13:48

それかデバッガを使って探るとか。 プロでもデバッグしていますよ♪
guest

回答2

0

また,もしも選択ソート以外におかしい部分がありましたらそちらも教えてください。

おかしい部分
0. fin>>score; で読めるのは "Shiroboshi" だけです。1行読めたつもりで
それをスペースやタブで vector<string> にしようとしてもダメです。
0. fin>>aite; も同じくダメです。
0. class Table の std::vector<Record> v; で、v.size() は 0。
v のサイズを増やすところがないから、void add(Record r) の中で
for文は回りません。v に r は追加できません。

とにかくどこまで思い通りに行っているのか、main からステップ実行して
すべての変数の値の変化を見てください。

追記
ifstream fin("file.txt"); int i; double d; string s; fin >> i >> d >> s;
とあった時、ファイルの内容が「123 4.5 abc」であれば、
i = 123、d = 4.5、s = "abc" と読み込めることは知っていますよね。

ifstream は istream から派生していて、istream の operator>> はデフォルトで
空白文字(スペース、タブ、改行など)を読み飛ばすモードになっています。
だから「123タブ4.5改行abc」であっても同じように読み込めます。

入力が「Yokozuna改行Hakuhou改行タブShiroboshiスペースShiroboshi改行Daieishou」だと、

fin >> rank; fin >> name; fin >> score; fin >> aite;
rank = "Yokozuna"、name = "Hakuhou"、score = "Shiroboshi"、aite = "Shiroboshi" となります。
score に「タブShiroboshiスペースShiroboshi改行」の 1行を読み込ませたかったら
getline を使います。

ところが、fin >> name; のとき "Hakuhou" の次の改行がまだ入力ストリームに
残ったままなので、getline(fin, score); ではその「改行」だけが読み込まれ、
「改行」は削除されるので、score = "" となります。
getline(fin, blank); getline(fin, score); のようにすると、
score には 1行の文字列が入力できます。

ファイルの最初の 2行の「Yokozuna改行Hakuhou改行」にスペースがないのであれば
getline(fin, rank);
getline(fin, name);
getline(fin, score);
getline(fin, aite);
getline(fin, blank);
で思い通りに読み込めます。

追記2
どんなふうにデバッグしているか教えてください。

どこで Segmentation falut しているか、
デバッガを使っているのなら、すぐに分かります。

デバッガを使えないなら、次のようにすれば、範囲を絞れます。

C++

1 import(fin,table); 2std::cout << "import done\n"; 3 table.print(); 4std::cout << "print done\n"; 5 table.sort(); 6std::cout << "sort done\n"; 7 table.result(fout); 8 return 0;

実行結果

text

1import done 2print done 3Segmentation fault (core dumped)

table.sort() の中で Segmentation fault していますね。
table.print() は問題ない。いや、問題大ありです。
読み込んだものが何も表示されないなんて変です。
では、print() が間違っているのか?
そうではなくて、import() で table に何も読み込んでいないのかもしれません。
split() が正しく動いるかどうか確かめてください。
return v; の前に、
cout << "v.size() = " << v.size() << "\n";
for (auto e : v) cout << e << endl;
を入れてみてください。

とにかく、どんなふうにデバッグしているかを教えてください。

追記3

C++

1class Table { 2 friend Record; 3 std::vector<Record> v; 4public: 5 void add(Record r) { v.push_back(r); } 6 ... 7}; 8 9#include <sstream> // istringstream 10 11std::vector<std::string> split(std::string s) 12{ 13 std::vector<std::string> v; 14 std::istringstream iss(s); 15 std::string t; 16 while (iss >> t) v.push_back(t); 17 return v; 18} 19 20void import(std::ifstream & fin, Table & table) 21{ 22 while (true) { 23 Record record; 24 std::string s; 25 if (!getline(fin, s)) break; 26 record.set_r(s); 27 if (!getline(fin, s)) break; 28 record.set_n(s); 29 if (!getline(fin, s)) break; 30 record.set_s(split(s)); 31 if (!getline(fin, s)) break; 32 record.set_a(split(s)); 33 table.add(record); 34 getline(fin, s); 35 } 36}

どんなふうにデバッグしているかを教えてください。

投稿2021/06/08 17:24

編集2021/06/11 12:07
kazuma-s

総合スコア8224

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

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

wagashi_157

2021/06/09 05:50

fin>>の後にはどういった表現ができるのかがイマイチよく分かっていません。正しく読み込むにはどのようにすれば良いのでしょうか。
wagashi_157

2021/06/11 03:21 編集

split()関数でreturn v;の前に   cout << "v.size() = " << v.size() << "\n";   for (auto e : v) cout << e << endl; を入れてみました。その結果 v.size()=1 Shiroboshi Shiroboshi Fusenpai Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi Yasumi v.size()=1 Daieishou Takarafuji Ounoshou v.size()=1 Kuroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi Shiroboshi Shiroboshi Kuroboshi Shiroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi v.size()=1 Mitakeumi Ounoshou Takarafuji Hokutofuji Wakatakakage Meisei Shimanoumi Kiribayama Daieishou Myougiryu Takayasu Takanoshou Terunofuji Takakeishou Asanoyama ・・・・・・ v.size()=1 Kuroboshi Shiroboshi Shiroboshi Shiroboshi Shiroboshi Kuroboshi Kuroboshi Kuroboshi Shiroboshi Kuroboshi Shiroboshi Kuroboshi Shiroboshi Shiroboshi Kuroboshi v.size()=1 Hidenoumi Akua Tsurugishou Chiyoshouma Daiamami Akiseyama Toyoyama Chiyonoou Kagayaki Houshouryu Terutsuyoshi Kotoekou Midorifuji Chiyotairyu Tobizaru import done print done Segmentation fault と出力されました。 つまり, split関数(vector)にgetline(fin,score)やgetline(fin,aite)を正しく格納できていないということでしょうか。
kazuma-s

2021/06/10 16:58

自分の期待する結果は何ですか? デバッグするときは、入力ファイルの行数を 5行とか 10行にしてやると楽になりますよ。 vector関数? vector はクラスです。 関数split 内で、vector<string> クラスの変数 v を用意して、その v に処理結果を格納したい。 引数の文字列 s が " Shiroboshi Shiroboshi Fusenpai" だったとして、 v.size() = 3、v[0] = "Shiroboshi", v[1] = "Shiroboshi", v[2] = "Fusenpai" と なっている v が欲しいのではないのですか?
wagashi_157

2021/06/11 03:44 編集

すみません,一部表記が間違っていたのでコメントしますが,vector関数ではなくsplit関数(vector)の方にgetline(fin,score)やgetline(fin,aite)を正しく格納,つまり,例で示してくださったs= " Shiroboshi Shiroboshi Fusenpai"の時にv.size() = 3、v[0] = "Shiroboshi", v[1] = "Shiroboshi", v[2] = "Fusenpai"となっているvを作成したいです。 そして,最終的にはそれを読み込み,選択ソートを利用して勝ち数が多い順(同じ場合は負け数が少ない順)にresult.txtというまた別のファイルに出力したいです。(また,table.print()では定義で示した通りに出力したいです。)
kazuma-s

2021/06/11 03:53

欲しい v が得られないことで、split が間違っていることははっきりしています。 なぜ、split の処理を見直さないのですか? s = " Shiroboshi Shiroboshi Fusenpai" で始めてみましょう。 auto iter{s.begin()}; で、 iter は s[0] の ' ' の位置を持つようになりました。 while(iter!=s.end()){ で、iter は s.end() ではないのでループの中に入ります。 auto last=iter; で、last は s[0] を指します。 while (last!=s.end()&&(*last!=sep||*last!=sep2)) で、 last は s.end() に等しくない。OK。 *last は s[0] で ' '。sep と等しい。sep2 とは等しくない。 この while ループの中に入り、++last; により、last は s[1] を指すようになります。 last は s.end() に等しくない。OK。 *last は s[1] で 'S'。sep とは等しくない。|| の右は見なくていい。 この while ループの中に入り、++last; により、last は s[2] を指すようになります。 このようにして変数 last がどのように変化していくかを見れば済む問題ではありませんか? なぜ、やらないのですか? s = " abc def" でやったほうが簡単ですよ。
wagashi_157

2021/06/11 10:13

split()関数に文字列を格納する方法は解決しました。ありがとうございました。 一方で,scoreやaiteを含めimport関数の中で定義したstringをどのようにしてRecordクラスに格納するのかが未だに解決できていません。setterで格納するのか,それとも別の手段があるのかについてお聞きしたいです。
guest

0

自己解決

push_backの表し方とrecordクラスに格納する方法
push_backはクラス型のvectorに入れるため,push_back()の()に当てはまるのはクラスしか入らないので,void add関数の定義内に入れました。また,ファイルの読み込みの際にはgetline()を用いて行単位で読み込むプログラムを作成し,recordクラスに格納する際にはsetterを使いました。私は最初setterをvoid add関数に入れていたため,上手くいかないことが分かりました。

デバッグで試したこと
例えば,ファイルの読み込みが上手くいかない時,自分で定義したファイルを読み込む関数とmain関数だけを取り出して実行しました。そして,それでも上手くいかなかった時はそれぞれの行がどのような操作をしているのかをイメージしながらたどりました。他にも,split関数はvectorで返すものだったので,v.size()やそれぞれの要素の出力を行った結果,v.size()=1と誤った数値が出力されました。よって,split関数に原因があることに気づき見ていったのですが,' 'や'\t'で区切る条件を' '||'\t'としていたことでv.size()=1と出力されてしまったことが分かりました。具体例で試しに3つの単語をstringで表し(s="Shiroboshi Shiroboshi Kuroboshi"),それを&&に変えたらv.size()=3と正しく出力され,それぞれの要素も正しく出力されました。

投稿2021/06/11 16:55

wagashi_157

総合スコア51

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問