【问题标题】:Reading .txt file into array of struct将 .txt 文件读入结构数组
【发布时间】:2019-11-20 07:00:51
【问题描述】:

我是编程初学者,我正在尝试将我的 .txt 文件读入该程序中的结构数组,然后显示数据然后对其进行排序,但程序只读取第一行和直到 arraysize 循环才会停止。 文件数据如下所示:

ID NAME ADDRESS AGE

代码:

#include <iostream>
#include <fstream>
#include <string>
#include <conio.h>
using namespace std;

struct bio
{
    char name[50], address[50];
    int id, age; 
};

int main() 
{   
    int i = 0, arraysize = 1000;
    bio b[arraysize];
    fstream data;
    data.open("biodata.txt");
    while(data.read((char*)&b, sizeof(b[i])))
    {
        for (i = 1; i < 1000; i++)
        {
            data >> b[i].id >> b[i].name >> b[i].address >> b[i].age;
        }
    }
    for (i = 0; i < 1000; i++)
    {
        cout << b[i].id << " " << b[i].name << " " << b[i].address << " " << b[i].age << " " << endl;
    }
    data.close();
    getch();
}

【问题讨论】:

  • 你应该在data.read()data &gt;&gt;之间选择,因为他们都在做“阅读”
  • 如果可以的话,你真的应该避免使用古老的非标准编译器。我的猜测是您使用的是 Turbo C++。考虑使用 gcc 或 clang。
  • 您的代码存在不止一个问题。最重要的是:你不懂阅读。 read 用于二进制读取。使用while( data &gt;&gt;...
  • 你能给我这个文件的例子吗?我问是因为该文件的格式确实很重要

标签: c++ arrays struct


【解决方案1】:
#include <iostream>
#include <fstream>
#include <string>

#define ARRAY_SIZE 1000
#define FILE_NAME "biodata.txt"

using namespace std;

struct Bio
{
    int     m_id;
    string  m_name;
    string  m_address;
    int     m_age; 
};

int main() 
{ 
    Bio bio[ARRAY_SIZE];
    ifstream data;
    data.open(FILE_NAME);

    if (!data)
    {
        cout << "not file " << FILE_NAME;
        return 0;
    }

    for (int i = 0; i < ARRAY_SIZE && data.good(); ++i)
    {
        data >> bio[i].m_id >> bio[i].m_name >> bio[i].m_address >> bio[i].m_age;
    }

    for (int i = 0; i < ARRAY_SIZE ; ++i)
    {
        cout << bio[i].m_id << " " << bio[i].m_name << " " << bio[i].m_address << " " << bio[i].m_age << " " << endl;
    }

    data.close();
}

几个cmets:

  1. 使用什么 conio lib?
  2. struct (bio) 以大写字母开头
  3. 不要在 C++ 的 char 数组中使用,你有这个字符串。
  4. 将变量分隔为单独的行(bot "char name[50], address[50];")
  5. 最好将成员重命名为 m_X
  6. 关于您的“数组大小”。如果它是您决定的 const 数字,请使用#define 来执行。如果您需要整个文件,则根本不需要它。 (也是文件名)
  7. ifstream 而不是 fstream 数据。你只需要阅读。你不想因为一些错误而改变你的数据。
  8. 检查文件打开是否正常
  9. 在您的代码中,您检查循环之前的 while。
  10. 在您的条件循环中检查 data.good()。它检查它不是 eof 并且他的文件是可读的。
  11. 读取命令用于二进制文件
  12. 最好将加载文件和打印数据分开到 2 个不同的函数中。我这样做不是为了保存在您的模板上

【讨论】:

    【解决方案2】:

    以下对于初学者来说可能有点复杂,但既然我们在谈论 C++,我们也应该寻找一种“更”面向目标的方法。

    您设计了一个名为 bio 的课程。在面向对象的语言中,您将把对象的所有数据以及对这些数据进行操作的所有函数都放在类中。所以你需要添加成员函数。这个想法是将所有数据封装在一个对象中。外界应该对上课的细节一无所知。您只需通过成员函数访问它。如果您想稍后进行更改,您将在类的成员函数中执行此操作。程序的其余部分将继续工作。

    此外,我们绝对应该使用 C++ 语言功能。例如,您应该将 std::string 用于字符串,而不是普通的旧 C 样式字符数组。您基本上不应该在 C++ 中使用 C 样式数组。请改用 STL 容器。

    那么,让我们设计一个包含数据成员和成员函数的类。由于目前我们只需要输入和输出功能,因此我们覆盖了插入器和提取器操作符。这些操作员知道类的数据和行为,并且会小心。

    查看以下程序:

    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <algorithm>
    #include <sstream>
    
    struct Bio
    {
        // Data
        unsigned int id{}; 
        std::string name{};
        std::string address{};
        unsigned int age{};
    
        // Overload extractor operator to read all data
        friend std::istream& operator >> (std::istream& is, Bio& b) {
            std::string textLine{};
            if (std::getline(is, textLine)) {
                std::istringstream textLineStream{textLine};
                textLineStream >> b.id >> b.name >> b.address >> b.age;
            }
            return is;
        }
    
        // Overload inserter operator to print the data 
        friend std::ostream& operator << (std::ostream& os, const Bio& b) {
            return os << b.id << " " << b.name << " " << b.address << " " << b.age;
        }
    };
    
    std::istringstream sourceFile{R"(1 John Address1 31
    2 Paul Address2 32
    3 Ringo Address3 33
    4 George Address4 34
    )"};
    
    int main()
    {
        // Define Variable and read complete source file
        std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
    
        // Sort the guys by name
        std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
    
        // Show output on screen
        std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
    
        return 0;
    }
    

    一些cmets。在 StackOverflow 上,我无法使用文件。所以在我的示例程序中,我改用std::istringstream。但这也是std::istream。您也可以使用任何其他std::istream。所以如果你定义了一个```std::ifstreamto read from a file, then it will work in the same way as thestd::istringstream```。

    请看。提取器操作符完成读取源文件的全部工作。它是封装的。没有外部函数需要知道它是如何工作的。

    在 main 函数中,我们定义了一个 std::vector 并使用它的范围构造函数来指定数据的来源。我们给它std::istream_iterator,它会遍历输入数据并调用提取器操作符,直到读取所有内容。

    然后我们按名称排序并将结果复制到输出。

    您可能会注意到输入数据中的字段以空格分隔。这通常不适用于非原子数据。名称可以由两部分组成,地址可以包含街道和城市。为此,我们发明了 CSV(逗号分隔值)文件。

    请看下面更真实的灵魂。

    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <algorithm>
    #include <sstream>
    #include <regex>
    
    struct Bio
    {
        // Data
        unsigned int id{}; 
        std::string name{};
        std::string address{};
        unsigned int age{};
    
        // Overload extractor operator to read all data
        friend std::istream& operator >> (std::istream& is, Bio& b) {
            std::string line{}; 
            std::regex re{";"};
            if (std::getline(is, line)) {
                std::vector<std::string> token{std::sregex_token_iterator(line.begin(), line.end(), re, -1), std::sregex_token_iterator()};
                if (4 == token.size()) {
                    b.id = std::stoul(token[0]); 
                    b.name = token[1]; 
                    b.address = token[2];
                    b.age = std::stoul(token[3]); 
                }
            }
            return is;
        }
    
        // Overload inserter operator to print the data 
        friend std::ostream& operator << (std::ostream& os, const Bio& b) {
            return os << b.id << ", " << b.name << ", " << b.address << ", " << b.age;
        }
    };
    
    
    std::istringstream sourceFile{R"(1; John Lenon; Street1 City1; 31
    2; Paul McCartney; Street2 City2; 32
    3; Ringo Starr; Street3 City3; 33
    4; George Harrison; Address4; 34
    )"};
    
    int main()
    {
        // Define Variable and read complete source file
        std::vector<Bio> bio{std::istream_iterator<Bio>(sourceFile), std::istream_iterator<Bio>()};
    
        // Sort the guys by name
        std::sort(bio.begin(), bio.end(), [](const Bio& b1, const Bio& b2){ return b1.name < b2.name;});
    
        // Show output on screen
        std::copy(bio.begin(),bio.end(),std::ostream_iterator<Bio>(std::cout, "\n"));
    
        return 0;
    }
    

    我们有一个新的源格式并且 main 没有改变。只是提取器运算符被修改。这里我们使用不同的迭代器来获取源数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-15
      • 2016-06-11
      • 2015-04-16
      • 2013-12-11
      • 1970-01-01
      • 2021-10-23
      • 2013-12-16
      相关资源
      最近更新 更多