从用换行符和逗号分隔的txt文件中读取

Read from txt file delimited by newlines and commas

本文关键字:txt 文件 读取 分隔 换行符      更新时间:2023-10-16

我试图从一个txt文件中读取信息,但有些问题没有解决。我需要读取每一行,并将其保存在属于类Bilhete的变量中。

Alexandre
20/12/2015
1,2,3,4
Luis
30/10/1990
3,4,5,6

这是我的txt。

这代表一张票(葡萄牙语为bilhette):-所有者名称-过期日期-我可以参加的比赛。每个数字代表比赛的id。我的项目中有一门竞赛课,但我认为这对这个问题不是很重要

void Campeonato::readFileBilhetes(string filename) {

    ifstream ficheiro_leitura(filename.c_str());
    vector<Prova*> vecprovas = calendario->getProvas();
    vector <int> indices; //vector with competitions id
    if(!ficheiro_leitura)
        throw ErroNoFicheiro(filename);
    else{
        string dono, data, provasNoBilhete, vendido; //owner, date, competitions in the ticket, 
        while (!ficheiro_leitura.eof()){
            getline(ficheiro_leitura, dono);
            getline(ficheiro_leitura, data);
            getline(ficheiro_leitura, provasNoBilhete);

            stringstream dataSs;
            date d;
            dataSs << data;
            int dia, mes, ano;  //day, month, //year
            char tmp;
            dataSs >> dia >> tmp >> mes >> tmp >> ano;
            if(dia < 1 || dia > 31|| mes < 1 || mes > 12 || ano < 1 || (dia > 28 && mes == 2) || (dia > 30 && mes == 4) || (dia > 30 && mes == 6) || (dia > 30 && mes == 9) || (dia > 30 && mes == 11)){
                cout << "Data invalida!";
            }
            else{
                d.dia = dia; //day
                d.mes = mes; //month
                d.ano = ano; //year
            }

            string s1 = "";
            string s2= "";

            stringstream provasS;
            int i = 0;
            while (provasNoBilhete[i] != 'n' && provasNoBilhete[i] != ''){
                if(provasNoBilhete[i] == ','){
                    int indice;
                    provasS.clear();
                    provasS.str("");
                    provasS.str(s2);
                    provasS >> indice;
                    indices.push_back(indice);


                }
                else
                    provasS << provasNoBilhete[i];

                ++i;
                Bilhete *bi1 = new Bilhete(d, dono, indices);
                inserirBilhete(*bi1); //insert
            }

        }
    }
}

如何正确保存这些值?

向致以最良好的问候

我不确定您在读取文件时到底遇到了什么值的问题,但这里有一个示例,展示了如何读取您试图获得的值。这将正确地将名称读入字符串,将日期读入单独的整数,并将竞争id读入整数向量。我发现使用iss.peek方法对调试stringstream的非常有帮助。在这里,你可以看到我正在使用这个方法来确保我正在读取一个有效的文件。

另外,正如Beta所说,我建议不要使用file.eof()。尝试在循环的条件中使用getline

fstream infile(file);
string linha, dono;
int dia, mes, ano;
vector<int> campeonatoIds;
while (getline(infile, dono)) // get name and check status file stream
{
    cout << dono << endl;
    // read in date
    getline(infile, linha);
    int tmp;
    istringstream iss (linha);
    iss >> dia;
    if (iss.peek() != '/')
    {
        cerr << "incorrectly formatted file" << endl;
        exit(EXIT_FAILURE);
    }
    iss.ignore();
    iss >> mes;
    if (iss.peek() != '/')
    {
        cerr << "incorrectly formatted file" << endl;
        exit(EXIT_FAILURE);
    }
    iss.ignore();
    iss >> ano;

    cout << dia << "/" << mes << "/" << ano << endl;
    // read in competition ids
    getline(infile, linha);
    istringstream iss2(linha);
    while (iss2.peek() != ',' && iss2.good())
    {
        iss2 >> tmp;
        cout << tmp << ",";
        campeonatoIds.push_back(tmp);
        iss2.ignore();
    }
    cout << endl;
}

但是,我个人认为在这种情况下使用getline比使用stringstream更容易、更干净。以下是重构为使用getline的相同代码。

fstream infile(file);
string linha, dono;
int dia, mes, ano;
vector<int> campeonatoIds;
while (getline(infile, dono)) // get name and check status file stream
{
    cout << dono << endl;
    // read in date
    getline(infile, linha);
    istringstream iss (linha);
    string tmpstr1;
    getline(iss, tmpstr1, '/');
    dia = atoi(tmpstr1.c_str());
    getline(iss, tmpstr1, '/');
    mes = atoi(tmpstr1.c_str());
    getline(iss, tmpstr1, '/');
    ano = atoi(tmpstr1.c_str());
    cout << dia << "/" << mes << "/" << ano << endl;
    // read in competition ids
    getline(infile, linha);
    istringstream iss2(linha);
    string tmpstr2;
    while (getline(iss2, tmpstr2, ','))
    {
        campeonatoIds.push_back(atoi(tmpstr2.c_str()));
    }
    for (auto it : campeonatoIds)
    {
        cout << it << ",";    
    }
    cout << endl;
    campeonatoIds.clear();
}

正如您所看到的,我在while循环中使用getline而不使用,这允许您检查文件的有效性。此外,我认为使用getline可以为文件流的简单分离提供一个可选的分隔符。

我首先会为ticket类重载operator>>,这样它就有了读取单个票证的主体。然后我会用那个操作符从流中读取门票。

该标准已经定义了一个get_tgime(和put_time)操纵器,用于将时间从流读取到struct tm中,因此我们不妨使用它们。

代码可能看起来像这样:

friend std::istream &operator>>(std::istream &is, ticket &t) {
    std::getline(is, t.name);
    is >> std::get_time(&t.date, " %d / %m / %Y ");
    std::string temp;
    std::getline(is, temp);
    t.events = parse_list<int>(temp);
    return is;
}

parse_list将是这个一般顺序上的东西:

template <class T>
std::vector<T> parse_list(std::string const &in) {
    T temp;
    std::vector<T> ret;
    std::istringstream buf(in);
    while (buf >> temp) {
        ret.push_back(temp);
        buf.ignore(1);
    }
    return ret;
}

有了这些定义,读取一个充满ticket数据的文件并将其全部放入vector中会看起来像这样:

std::vector<ticket> tickets { std::istream_iterator<ticket>(input),
    std::istream_iterator<ticket>() };