简体   繁体   English

C ++:如何从.txt文件的一行中读取每行中变量数量未知的行?

[英]C++: How do I read in a line from a .txt file with an unknown number of variables on every other line?

I'm working on a project where I need to create a Linked List from a given data file. 我正在一个项目中,需要从给定的数据文件创建链接列表。 It contains operation codes, first names, last names, addresses, cities, states, and zip codes. 它包含操作代码,名字,姓氏,地址,城市,州和邮政编码。 The problem is that there is an unknown number of spaces on the lines that contain an address and the city. 问题在于,包含地址和城市的线路上的空格数量未知。 An example of the data file is as follows: 数据文件的示例如下:

A
Donald
Duck
1123 Appleberry Circle
Saint Cloud
MN
88084
A
Barry
Briches
112 New York Ave
Saint Cottleville
FL
78098

I've tried using a combination of: 我尝试使用以下组合:

infile >> FirstName >> LastName;
infile.getline(Address, 20);
infile.getline(City, 20);
infile >> State >> ZipCode;

but when I go to print it out, the output comes out really weird. 但是当我打印出来时,输出结果确实很奇怪。 Any help on the issue would be greatly appreciated. 在这个问题上的任何帮助将不胜感激。 All code I have done is attached. 我完成的所有代码均附后。

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string.h>
#include "list.h"

using namespace std;

int main()
{
    ifstream infile("data2.txt", ios::in);
    ofstream outfile("output2.txt", ios::out);

    char opCode, fname[12], lname[12], address[20], city[12], state[4], 
    zip[6];

    MailListClass mailingList;

    infile >> ws >> opCode;

    while (opCode != 'Q')
    {
        switch (opCode)
        {
            case 'A' :  infile >> fname >> lname;
                        infile.getline(address, 20, '\n');
                        infile >> city >> state >> zip;
                        mailingList.addRecord(fname, lname, address, city, 
                        state, zip, outfile);
                        break;
            case 'P' :  mailingList.printRecord(outfile);
                        break;
        }
        infile >> ws >> opCode;
    }
    return 0;
}

The .h and .cpp struct/class files: .h和.cpp结构/类文件:

#ifndef LIST_H
#define LIST_H

#include <fstream>

using namespace std;

struct node
{
    char lname[12], fname[12], city[12], address[20], state[4], zip[6];
    node *next;
};

class MailListClass
{
    private:
        node *headPtr, *currPtr, *prevPtr;
    public:
        MailListClass();
        void addRecord(char fname[12], char lname[12], char address[20],
                            char city [12], char state[4], char zip[6],
                            ofstream &outfile);
        void printRecord(ofstream &outfile);
};

#endif 

list.cpp list.cpp

#include <cstdlib>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>

#include "list.h"

using namespace std;

MailListClass::MailListClass()
{
    headPtr = NULL;
    currPtr = NULL;
    prevPtr = NULL;
}

void MailListClass::addRecord(char fname[12], char lname[12], char address[20], char city[12],
                            char state[4], char zip[6], ofstream &outfile)
{
    node *tail = new node;
    tail->next = NULL;
    strcpy(tail->fname, fname);
    strcpy(tail->lname, lname);
    strcpy(tail->address, address);
    strcpy(tail->city, city);
    strcpy(tail->state, state);
    strcpy(tail->zip, zip);

    if (headPtr != NULL)
    {
        currPtr = headPtr;
        while (currPtr->next != NULL)
        {
            currPtr = currPtr->next;
        }
        currPtr->next = tail;
    }
    else
    {
        headPtr = tail;
    }
} 
void MailListClass::printRecord(ofstream &outfile)
{
    currPtr = headPtr;
    while (currPtr != NULL)
    {
        outfile << left << setw(12) << currPtr->lname;
        outfile << setw(12) << currPtr->fname;
        outfile << setw(20) << currPtr->address;
        outfile << setw(12) << currPtr->city;
        outfile << setw(6) << currPtr->state;
        outfile << setw(10) << currPtr->zip << endl;
        currPtr = currPtr->next;
    }
}

The outfile should looks like this: 输出文件应如下所示:

Last Name    First Name    Address              City   State  Zip
Last Name    First Name    Address              City   State  Zip

but with the getline() in my code, my output looks closer to this: 但是在代码中使用getline()时,我的输出看起来更接近于此:

Last Name    First Name
                  Address#        AddressNameCityCity
Last Name    First Name
                  Address#        AddressName

I'm afraid that questions like this have asked very often. 恐怕这样的问题经常问到。 However, here my answer: 但是,这是我的答案:

I believe, this is the mis-concept: 我相信,这是错误的观念:

infile >> FirstName >> LastName;
infile.getline(Address, 20);

It reads FirstName and LastName but the question is where are the line-ends read? 它读取FirstNameLastName但问题是在哪里读取行尾?

A simplified sample to illustrate this: 一个简化的示例来说明这一点:

#include <iostream>
#include <string>

int main()
{
  std::string name1, name2;
  std::cin >> name1 >> name2;
  std::cout << "name1: '" << name1 << "', name2: '" << name2 << "'\n";
  for (std::string line; std::getline(std::cin, line);) {
    std::cout << "next: '" << line << "'\n";
  }
  return 0;
}

Tested on coliru with 在coliru上进行了测试

Donald
Duck
1123 Appleberry Circle

given as input (by echo -e ): 作为输入给出(通过echo -e ):

$ g++ -std=c++17 main.cpp && echo -e "Donald\nDuck\n1123 Appleberry Circle\n" | ./a.out
name1: 'Donald', name2: 'Duck'
next: ''
next: '1123 Appleberry Circle'
next: ''

Live Demo on coliru 在coliru上进行现场演示

The line read and printed after Duck is empty. Duck为空后读取并打印该行。 This is because the operator<<(std::istream&, std::string&) doesn't read the white space (eg space, tab, line-end) delimiting the text. 这是因为operator<<(std::istream&, std::string&)不会读取分隔文本的空格(例如,空格,制表符,行尾)。 Hence, the line-ends after Donald and Duck are left in input buffer. 因此, DonaldDuck之后的行尾保留在输入缓冲区中。 After first string input ( Donald >> name1 ), it doesn't hurt. 在第一个字符串输入( Donald >> name1 )之后,它没有问题。 The next input ( Duck >> name2 ) consumes/skips white spaces until a non-whitespace follows. 下一个输入( Duck >> name2 )占用/跳过空格,直到出现非空格。 After second input, the line-end stays in input buffer until the next call of std::getline() consumes it. 在第二次输入后,行尾将保留在输入缓冲区中,直到下一次调用std::getline()消耗掉它为止。

There are various ways to fix this: 有多种解决方法:

  1. Read everything with std::getline() (and, may be, use std::istringstream with input operators to process the read line buffer further). 使用std::getline()读取所有内容(并且可能与输入运算符一起使用std::istringstream来进一步处理读取行缓冲区)。

  2. Place a std::getline() after infile >> FirstName >> LastName; std::getline() infile >> FirstName >> LastName; to skip rest of line. 跳过其余部分。

  3. Use std::istream::ignore() which 使用std::istream::ignore()

    Extracts and discards characters from the input stream until and including delim. 从输入流中提取并丢弃字符,直到并包括delim。

The above sample fixed with std::istream::ignore() : 上面的示例使用std::istream::ignore()修复了:

#include <iostream>
#include <string>

int main()
{
  std::string name1, name2;
  std::cin >> name1 >> name2;
  std::cin.ignore();
  std::cout << "name1: '" << name1 << "', name2: '" << name2 << "'\n";
  for (std::string line; std::getline(std::cin, line);) {
    std::cout << "next: '" << line << "'\n";
  }
  return 0;
}

Tested on coliru again: 再次在coliru上测试:

$ g++ -std=c++17 main.cpp && echo -e "Donald\nDuck\n1123 Appleberry Circle\n" | ./a.out
name1: 'Donald', name2: 'Duck'
next: '1123 Appleberry Circle'
next: ''

Live Demo on coliru 在coliru上进行现场演示


Concerning the "asked very often", here some other questions: 关于“经常问”,这里还有其他一些问题:

There are probably much more. 可能还有更多。 I found the above by 我发现了以上
google site:stackoverflow.com c++ input missing google site:stackoverflow.com c++ input missing

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM