0

コンマで区切られた 3 つの変数を抽出するために get 行を使用して csv を読み込もうとしています。名前、コース、グレード。

私は最初の行をうまく読んでいますが、奇妙な新しい改行を入れて、フォーマットをクラスターに送信します。

これが私のコードです:

#include "header.h"

string student::GetCourse() {
    return course;
}

string student::GetName() {
    return name;
}

string student::GetGrade() {
    return grade;
}

void student::setname(string n) {
    name = n;
}

void student::setCourse(string c) {
    course = c;
}

void student::setGrade(string g) {
    grade = g;
}
void sort (vector <student> &List) {

    student temp;
    int first = 1;
    int vectorLength = List.size() - 1;

    for (int i = vectorLength; i > 0; i--) {
        first = i;
        for (int j = 0; j < i; j++) {
            if (List[j].GetName() > List[first].GetName())
            first = j;
        }
        temp = List[first];
        List[first] = List[i];
        List[i] = temp;
    }

}

void main () {
    ifstream file;
    vector <student> StudentList;

    file.open("short.txt");

    while (!file.eof()) {

        file.ignore(8196,'\n');

        string tempname, tempgrade, tempcourse = "";

        if (file != "\n") {
            getline(file, tempname, ',');
            getline(file, tempcourse, ',');
            getline(file, tempgrade, ',');
        }

        student s;
        s.setCourse(tempcourse);
        s.setname (tempname);
        s.setGrade (tempgrade);

            StudentList.push_back(s);

    }
    //sort (StudentList);

    for (int i = 0; i < StudentList.size(); i++) {
        cout << StudentList[i].GetName() << " " << StudentList[i].GetCourse() << " " << StudentList[i].GetGrade() << endl;
    }
}

どんなアイデアでも、私はこのファイルを読むのに本当に苦労しています。

4

5 に答える 5

7

さて、ここに行きます

  • if (file != "\n")比較はナンセンスです。それはあなたが思っていることをしません。
  • グレードの後の区切り文字は ではなく','、 です'\n'
  • while (!file.eof())間違っています。すでに発生した後にのみEOFをチェックします。getline()代わりにの戻り値を確認する必要があります

また

  • 通常は C++ で行いますstd::ifstream file("short.txt");open()別途お電話いただく必要はございません。
  • ""に初期化する必要はありませんstd::string。それは自動的に起こります。そうしなければならなかったとしても、書くべきだった

    std::string a = "", b = "", c = "";.

    もしそうならstd::string a, b, c = "something"、 c だけが何かに初期化されます。

于 2009-11-18T17:13:18.997 に答える
5

いくつかのコメント:

独自のソートを記述しないでください。

STL には、独自のソート アルゴリズムが組み込まれています。
オブジェクト間の関係を指定するだけです。

bool operator<(student const& lhs,student const& rhs)
{
    return lhs.GetName() < rhs.GetName();
}
// Now a sort is:

   std::sort(list.begin(),list.end());

使用しないでください: while (!file.eof())

これは、ファイルを読み取るための標準的なアンチ パターンです。
問題は、テストには早すぎるか、2 つ遅すぎることです。何も読んでいない場合は、何も起こっていないので 2 時間前です。何かを読んだことがある場合は、読んだアイテムの処理を行った (しかし失敗した) ため、既に手遅れです。

最善の方法は、読み取りを while ループに入れることです。これは、読み取りの結果がストリームへの参照を返すためです。これは、ブール コンテキストで使用できるオブジェクトに自動的に変換できます (ストリームに問題があるかどうかを確認するための変換テスト)。そのため、読み取りに失敗すると、ストリームはブール値のコンテキストで false に相当するものに変換される状態になります。

std::string line;
while(std::getline(file,line))
{
   // loop only entered if getline() worked.
   // Thus we know that we have a good value in line.
   // use line
}

マジック ナンバーは使用しないでください。

あなたは本当に 8000 文字を無視していますか、それとも単に行を削除しようとしていますか?

file.ignore(8196,'\n');

2 つの選択肢があります。

std::string ignoreLine;
std::getline(file,ignoreLine);

// Dont use a magic number but use a number that is guranteed not to fail.
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n')

怠惰にならないでください:

プログラミングの主なことは、保守可能なコードを書くことです。
この種の初期化を使用することは、(比較的普遍的に)単に怠惰であると非難されます。各宣言を別の行に入れます。コードが読みやすくなります。

string tempname, tempgrade, tempcourse = "";

// Like this:
std::string tempname;
std::string tempgrade;
std::string tempcourse;

stringstream を使用して、行を部分に分割します

ここで何をしようとしているのかわかりませんか?

if (file != "\n")
{   getline(file, tempname, ',');
    getline(file, tempcourse, ',');
    getline(file, tempgrade, ',');
}

上記のループと組み合わせると読みやすくなると思います。

std::string line;
while(std::getline(file,line))
{
    std::stringstream  linestr(line);

    if (getline(linestr, tempname, ',') &&
        getline(linestr, tempcourse, ',') &&
        getline(linestr, tempgrade, ',')
       )
    {
        // Here we have read a line.
        // And successfully retrieved three comma separated values from the line
    }
}

機会があれば、ループを標準アルゴリズムに置き換えます

この出力ループは std::copy() で置き換えることができます

for (int i = 0; i < StudentList.size(); i++)
{        cout << StudentList[i].GetName() << " " 
              << StudentList[i].GetCourse() << " " 
              << StudentList[i].GetGrade() << endl;
}

必要なのは、クラスの出力演算子を定義することだけです。

std::ostream& operator<<(std::ostream& str,student const& data)
{
    str << data.getName() << " "
        << data.getCourse() << " "
        << data.getGrade() << " "; // No newline here.
    return str;
}

これで、ベクトルを std::cout にコピーできます

std::copy(StudentList.begin(),StudentList.end(),
          std::ostream_iterator<student>(std::cout,"\n")
         );

主なバグ。

私が目にする主なバグは次の行です。

if (file != "\n")

ここでは、ファイルを 'C-string' と比較します。コンパイラがこれをコンパイルする方法はわかりません。
いくつかのオプションが思い浮かびますが、それが明らかでないという事実は、バグの原因となる可能性があります。また、これは 2 つの文字列を比較する方法ではないことに注意してください (一方が std::string でない限り)。

コンパイラはファイルをポインターに変換し、それを「C-String」と比較すると思います(これも単なるポインターであるため)。少し変だと思うかもしれませんが、ファイルを void* に変換する演算子があります。ポインターは意味のあるものを指していませんが、NULL であるか NULL ではないかのいずれかであり、char* ポインターと比較できるため、true になります (文字列 "\n" と等しくならないため)。

于 2009-11-18T22:05:18.067 に答える
2

最初:入力がどこでも成功するかどうかをチェックしていません。ファイルを開くことができるかどうかさえチェックしません。

int main () {                          // it's int main()!
  ifstream file("short.txt");
  if(!file.good()) {
    std::cerr << "couldn't open \"short.txt\"\n";
    return 1;
  }

  vector <student> StudentList;
  for(;;) {
    // ...
  }
  if( !file.eof() ) {
    std::cerr << "error reading before eof!\n";
    return 2;
  }
  // ...
}

次に: 通常、そのループ内の行を最初に読み取る方が簡単です。

for(;;) {
  std::string line;
  std::getline(file, line);
  if(!file) break;
  // ...
}

次に、これらの行から文字列ストリームを介して読み取ります。行単位で読み取るコードを独自の関数に入れます。

std::istream& read_line(std::istream& is, StudentList& list)
{
  std::string value1, value2, value3;
  std::getline(is, value1, ',');
  std::getline(is, value2, ',');
  std::getline(is, value3, ',');
  if(is)
    StudentList.push_back(...);
}

// ...
for(;;) {
  std::string line;
  std::getline(file, line);
  if(!file) break;

  std::istringstream iss(line);
  read_line(iss, StudentList);
  if(!iss) break;
}
// ...

HTH。

于 2009-11-18T17:17:21.003 に答える
1

たくさんの回答をいただきました。彼らの提案は間違いなくあなたが現在行っていることよりも改善されていますが、私は彼らが提案したものとは少し異なる方法でこれを扱います.

現在、studentクラスは基本的に「ダムデータ」(つまり、単なる構造体)を模倣するために最善を尽くしていますが、構文が醜いです-各メンバーに get/set ペアを使用していますが、何も追加していません. クラス自体は、studentあたかも単純な構造体であるかのように「ダム」です。のすべてのロジックは、studentまだstudentクラスの外にあります。

有用にするために、クラスには、ストリームから a を読み取る方法や、別のストリームに a を表示するstudent方法など、関連するかなりの量のロジックが含まれている必要があります。studentstudent

class student { 
    std::string name, course, grade;
public:

    bool operator<(student const &other) const {
        return name < other.name;
    }

    friend std::ostream &operator<<(std::ostream &os, student const &st) { 
        return os << st.name << " " << st.course << " " << st.grade;
    }

    friend std::istream &operator>>(std::istream &is, student &st) { 
         std::string temp;
         is >> temp;
         std::istringstream t(temp);
         std::getline(t, st.name, ',');
         std::getline(t, st.course, ',');
         std::getline(t, st.grade);
         return is;
    }
};

これにより、 main がかなり単純になります。

int main() { 
    std::ifstream in("short.txt");
    std::vector<student> students;

    std::copy(std::istream_iterator<student>(in),
              std::istream_itertor<student>(),
              std::back_inserter(students));
    std::sort(students.begin(), students.end());
    std::copy(students.begin(), students.end(), 
        std::ostream_iterator<student>(std::cout, "\n"));
    return 0;
}               

student特に、main は「全体」を論理エンティティとして扱うだけであることに注意してください。オブジェクトのコンポーネント パーツの「内部」を見ることは一度もありません。student

于 2009-11-18T18:01:06.420 に答える
0

呼び出しで区切り文字を「,」に設定する

getline(file, tempname, ',');

一度に行全体を読んでいるわけではありません。「\n」はデフォルトの区切り文字であり、デフォルトを使用すると、行の一部だけでなく行全体が取得されます。

デフォルトの区切り文字を使用して行全体を読み取り、「,」を区切り文字として使用して行をトークンに分割し、if(!file.eof) ファイルの読み取りがいつ終了するかを判断するために使用することをお勧めします。

于 2009-11-18T17:25:44.387 に答える