无法从文件加载正确的信息 [英] Can't load correct information from file

查看:57
本文介绍了无法从文件加载正确的信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这段代码的问题是它没有正确读取.txt文件,我提供了.txt文件的图片,同时还提供了它给我的当前输出.任何帮助都将受到欢迎.

the problem with this bit of code is that it is not reading the .txt file correctly, I have provided a image of the .txt file while also providing the current output it is giving me. Any help would be welcomed.

#include <fstream>
#include <iostream>

using namespace std;
const int MAX_CHARS = 10;
const int MAX_STUDENTS = 1;
class File
{
public:
void openFile()
{
    ifstream input_file("UserPass.txt", ios::binary);
    if (input_file.fail())
    {
        cout << "Could not open file" << endl;
    }
    else
    {
        if (!input_file.read((char*)&studLoaded, sizeof(studLoaded)))
        {
            cout << "Could not read file" << endl;
        }
        else
        {
            streamsize bytesRead = input_file.gcount();
            if (bytesRead != sizeof(studLoaded))
            {
                cout << "Could not read expected number of bytes" << endl;
            }
            else
            {
                input_file.read((char*)&studLoaded, sizeof(studLoaded));
                input_file.close();
            }


        }
    }
};

void displayFile()
{
    for (size_t i = 0; i < MAX_STUDENTS; i++)
    {
        cout << "Username: " << studLoaded[i].username << endl;
        cout << "Password: " << studLoaded[i].password << endl;
        cout << "Verf ID:" << studLoaded[i].verfID << endl;
    }
}

private:

typedef struct
{
    char username[MAX_CHARS];
    char password[MAX_CHARS];
    int verfID;

}student_t;

student_t studLoaded[MAX_STUDENTS];
};

主要只是调用这些函数

File f;
f.openFile();
f.displayFile(); 

这是.txt文件中的内容

This is what is in the .txt file

这是我当前的输出.我尝试了很多事情,但似乎无法正常工作.这是我得到的当前输出.

This is the current output I have. i have tried numerous of things but I can't seem to get it to work. This is the current output I am getting.

推荐答案

从我上面的评论继续,鉴于您显示的输入文件是TEXT文件,您不想将其读取为 ios :: binary .为什么?读取二进制输入时,所有文本格式字符都没有特殊含义.您只是在读取数据字节,而'\ n'(值: 0xa )只是流中的另一个字节.阅读文本时,您想使用文本文件中的格式字符告诉您何时阅读一行或一个单词.

Continuing from my comment above, given the input file you show is a TEXT file, you don't want to read as ios::binary. Why? When reading binary input, all text formatting character have no special meaning. You are simply reading bytes of data, and '\n' (value: 0xa) is just another byte in the stream. When reading text, you want to use the formatting characters in the text file to tell you when you have read a line or a word.

此外,如 @sheff 所述,如果您要阅读二进制文件,则需要您事先了解如何您将读入 username password 的许多字节,以及 verfID int在流中的位置.他提供的链接很好地说明了该过程 C ++常见问题解答:序列化和反序列化.对于写入二进制数据,尤其是当数据位于 struct 中时,除非进行序列化,否则无法保证编译器之间的可移植性,因为可能会在其中插入填充位.

Further, as commented by @sheff were you to read in binary, it is up to you to know beforehand how many bytes you will read into username or password and where the verfID int is located in the stream. The link he provided gives a good explanation of the process C++ FAQ: Serialization and Unserialization. For writing binary data, especially when the data is in a struct, unless you serialize, there is no guarantee of portability between compilers due to where padding bits may be inserted into the struct.

因此,除非您需要以二进制形式进行读写,否则最好将文本文件读取为文本.

So, unless you have a requirement to read and write in binary, you are better served reading a text file as text.

通过重载<< >> 运算符以读取学生有价值的数据,可以使学生数据的读取和输出更加简单一次从输入流中以文本形式显示.例如,要重载<< 运算符以读取 student_t 数据的麻烦,您只需将成员函数添加到您的类中即可:

You can make the reading and outputting of your student data much simpler by overloading the << and >> operators to read a students worth of data at a time from your input stream as text. For example to overload the << operator to read a student_t worh of data, you can simply add a member function to your class:

    /* overload >> to read username, password, verfID from input stream */
    friend std::istream& operator >> (std::istream& is, passfile& pf)
    {
        student_t s {};     /* temporary struct student */

        /* attempt read of all 3 values (username, password, verfID) */
        if (is >> s.username >> s.password >> s.verfID) {
            /* handle storage of s here */
        }

        return is;  /* return stream state */
    }

使用重载运算符的好处不仅减少了必须编写的自定义输入函数,而且还大大减少了 main().例如:

The benefit of using the overloaded operators not only reduces the custom input functions to have to write, but will reduce your main() considerably. For example:

int main (int argc, char **argv) {

    if (argc < 2) { /* verify at least 1 argument for filename */
        std::cerr << "error: password filename required.\n";
        return 1;
    }

    passfile pf (argv[1]);      /* declare instance of class, with filename */
    std::cout << pf;            /* output all student data */
}

要将类的各个部分放在一起,请避免使用基本类型,例如 char [CONST] ,而不要使用STL提供的内容,例如

To put the pieces of your class together, avoid using fundamental types such as char[CONST] and instead what the STL provides such as std::string, std::vector (for your collection of student_t instead of a plain-old array of struct), etc. For your class, you will one additional container to force a unique verfID. You can write a function yourself to scan over all the collected student_t each time before insertion of a new student, or you can use std::unordered_set to do it for you in a much more efficient way.

因此,使用STL容器,您只需要 std :: vector< student_t> 来存储学生信息(而不是数组),就可以使用 std :: unordered_set< int> 对您的 verfID 进行哈希处理并强制唯一性.您的类 private:数据成员可能类似于:

So using the STL containers, you would simply need a std::vector<student_t> for your student information storage (instead of an array) and you would use std::unordered_set<int> to hash your verfID and enforce uniqueness. Your class private: data members could be something like:

class passfile {

    struct student_t {
        std::string username {}, password {};   /* user std:string istead */
        int verfID;
    };

    std::unordered_set<int> verfID;         /* require unique verfID before add */
    std::vector<student_t> students {};     /* use vector of struct for storage */
    ...

对于您的 public:成员,您可以使用一个构造函数,该构造函数以要读取的文件名作为参数,然后,除了重载的之外,您仅需要一个辅助函数.<< >> 运算符.helper函数只是循环使用重载的>> 运算符来获取输入,直到到达文件末尾为止.

For your public: members, you can use a constructor that takes the filename to read from as an argument, and then you would need only one helper-function in addition to the overloaded << and >> operators. The helper-function just loops taking input with the overloaded >> operator until you reach the end of file.

您的构造函数实际上并不需要:

Your constructors really don't need to be any more than:

  public:

    passfile() {}
    passfile (std::string fname) { readpwfile (fname); }
    ...

重复使用>> 运算符的帮助函数可以是:

Your helper-function to repeatedly use the >> operator can be:

    void readpwfile (std::string fname)     /* read all students from filename */
    {
        std::ifstream f (fname);
        do
            f >> *this;                     /* use overloaded >> for read */
        while (f);
    }
    ...

其余细节由重载的<< >> 运算符处理.从<< 的重载开始,除了循环遍历所有学生并以您喜欢的格式输出数据外,您真的不需要它做任何事情,例如

The remaining details are handled by the overloaded << and >> operators. Starting with the overload of <<, you really don't need it to do any more than loop over all students and output the data in the format you like, e.g.

    /* overload << to output all student data */
    friend std::ostream& operator << (std::ostream& os, const passfile& pf)
    {
        for (auto s : pf.students)
            os  << "Username: " << s.username << '\n' 
                << "Password: " << s.password << '\n' 
                << "Verf ID : " << s.verfID << "\n\n";

        return os;
    }

(注意:在类中的声明中使用的 friend 关键字,如果在其他位置定义了该函数,则将省略 friend 在定义之前)

(note: the friend keyword in used in the declaration within the class, if you defined the function elsewhere, you would omit friend before the definition)

您的>> 重载是大部分工作发生的地方,尽管逻辑很简单.您声明一个临时的 student_t 以便从流中读取值.如果成功,则在unordered_set中进行快速查找以查看 verfID 是否已经存在.如果不是,则将 verfID 添加到您的unordered_set中,然后将您的临时 student_t 添加到您的矢量中,然后完成.如果 verfID 是重复的,则可以发出警告或错误,例如

Your overload of >> is where most of the work takes place, though the logic is simple. You declare a temporary student_t to read values into from the stream. If that succeeds, you do a quick lookup in your unordered_set to see if the verfID is already present. If it isn't you add the verfID to your unordered_set, and add your temporary student_t to your vector and your are done. If the verfID is a duplicate, you can issue a warning or error, e.g.

     /* overload >> to read username, password, verfID from input stream */
    friend std::istream& operator >> (std::istream& is, passfile& pf)
    {
        student_t s {};     /* temporary struct student */

        /* attempt read of all 3 values (username, password, verfID) */
        if (is >> s.username >> s.password >> s.verfID) {
            /* if verfID not already in verfID unordered_set */
            if (pf.verfID.find (s.verfID) == pf.verfID.end()) {
                pf.verfID.insert (s.verfID);    /* add verfID to unordered_set */
                pf.students.push_back (s);      /* add temp student to vector */
            }
            else    /* warn on duplicate verfID */
                std::cerr << "error: duplicate verfID " << s.verfID << ".\n";
        }

        return is;  /* return stream state */
    }

在一个简短的示例中将其完全放入(基本上只是添加标题并关闭上面的信息的类),您将拥有:

Putting it altogether in a short example (which basically just adds the headers and closes the class for the information above), you would have:

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <utility>
#include <unordered_set>

class passfile {

    struct student_t {
        std::string username {}, password {};   /* user std:string istead */
        int verfID;
    };

    std::unordered_set<int> verfID;         /* require unique verfID before add */
    std::vector<student_t> students {};     /* use vector of struct for storage */

  public:

    passfile() {}
    passfile (std::string fname) { readpwfile (fname); }

    void readpwfile (std::string fname)     /* read all students from filename */
    {
        std::ifstream f (fname);
        do
            f >> *this;                     /* use overloaded >> for read */
        while (f);
    }

    /* overload >> to read username, password, verfID from input stream */
    friend std::istream& operator >> (std::istream& is, passfile& pf)
    {
        student_t s {};     /* temporary struct student */

        /* attempt read of all 3 values (username, password, verfID) */
        if (is >> s.username >> s.password >> s.verfID) {
            /* if verfID not already in verfID unordered_set */
            if (pf.verfID.find (s.verfID) == pf.verfID.end()) {
                pf.verfID.insert (s.verfID);    /* add verfID to unordered_set */
                pf.students.push_back (s);      /* add temp student to vector */
            }
            else    /* warn on duplicate verfID */
                std::cerr << "error: duplicate verfID " << s.verfID << ".\n";
        }

        return is;  /* return stream state */
    }

    /* overload << to output all student data */
    friend std::ostream& operator << (std::ostream& os, const passfile& pf)
    {
        for (auto s : pf.students)
            os  << "Username: " << s.username << '\n' 
                << "Password: " << s.password << '\n' 
                << "Verf ID : " << s.verfID << "\n\n";

        return os;
    }
};

int main (int argc, char **argv) {

    if (argc < 2) { /* verify at least 1 argument for filename */
        std::cerr << "error: password filename required.\n";
        return 1;
    }

    passfile pf (argv[1]);      /* declare instance of class, with filename */
    std::cout << pf;            /* output all student data */
}

示例输入文件

在上方将您的输入文件用作TEXT文件:

Using your input file above as a TEXT file:

$ cat dat/userpass.txt
Adam
Pass121
1
Jamie
abc1
2

使用/输出示例

运行程序并提供输入文件作为第一个参数将导致:

Running the program and providing your input file as the first argument would result in:

$ ./bin/passwdfile dat/userpass.txt
Username: Adam
Password: Pass121
Verf ID : 1

Username: Jamie
Password: abc1
Verf ID : 2

如果您需要通过提示用户输入信息来添加更多学生,则只需:

If you needed to add more students by prompting the user for information, then all that would be needed is:

    std::cout << "enter user pass verfID: ";
    std::cin >> pf;

(尝试一下,然后尝试添加重复的 verfID ...)

(try it, and try adding with a duplicate verfID...)

仔细检查一下,如果还有其他问题,请告诉我.到目前为止,使用STL提供的容器是更好的方法,而不是自己尝试重新发明轮子(这样可以避免很多错误...)

Look things over and let me know if you have further questions. Using the containers the STL provides is by far the better approach to take rather than trying to reinvent the wheel on your own (you eliminate a lot of errors that way...)

这篇关于无法从文件加载正确的信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆