如何从二进制文件中读取最后一行

how to read last line from binary file

本文关键字:一行 最后 读取 二进制文件      更新时间:2023-10-16

有一个二进制文件包含这样的数据(这两个文件都有字符串数据类型);

Tyler   Bred
Guus    Hiddink
Didier  Droggba 

我想读取位于文件末尾的数据(在这种情况下,确切地说是Didier和Droggba)。所以我写了一个代码来处理它,如下所示;

结构:

struct user
{
   string id;
   string pwd;
};

读取数据的代码:

void confirm(const char* f)
{
    user u;
    ifstream in;
    in.open(f, ios::in | ios::binary);
    if (!in)
    {
        cout << "failed to read file!" << endl;
        exit(0);
    }
    while (in >> u.id >> u.pwd)
    {
       if (in.eof())
       {
           cout << "Registration Complete. " << endl;
           cout << "Your user information:  " << endl;
           cout << "ID:" << u.id << " " << "Password: " << u.pwd << endl;
       }
    }
}

我运行了上面的代码,但没有成功。我什么都没有。所以我更改了这样的代码;

void confirm(const char* f,user* u)
{
    user u1;
    ifstream in;
    in.open(f, ios::in | ios::binary);
    while (in >> u1.id >> u1.pwd)
    {
        if (!in)
        {
            cout << "failed to open file" << endl;
            exit(0);
        }
        if((u1.id == u->id)&& (u1.pwd == u->pwd))
        {
            cout << "Registration Complete." << endl;
            cout << "You registered;" << endl;
            cout << "ID:" << u1.id << "," << "Pwd: " << u1.pwd << endl;
        }
        delete u;
    }
}

不幸的是,这并没有奏效。我收到"访问违规…"错误消息。

我尝试了许多其他代码来解决这个问题,但这些东西也不起作用。我认为问题是:如果(in.of())但我不知道该如何改变它来解决这个问题。如果有人指出我的错误或给我小费,我将不胜感激。

文件中的每一行末尾通常都有一个换行符(在某些操作系统中为字符***s***)。您的代码。。。

while (in >> u.id >> u.pwd)
{
   if (in.eof())
   {
       cout << "Registration Complete. " << endl;
       cout << "Your user information:  " << endl;
       cout << "ID:" << u.id << " " << "Password: " << u.pwd << endl;
   }
}

假设您在读取最后一个人的名字后会点击eof,但当您读取该姓氏时,如果该行有一个换行符,您还没有点击eof(),那么下一个while (in >>...测试将失败,并跳过while-循环体,永远不会进入if (in.eof())

你能做的最小的改变来修复你的代码:

while (in >> u.id >> u.pwd >> std::skipws)
{
   if (in.eof())
   {
       cout << "Registration Complete. " << endl;
       cout << "Your user information:  " << endl;
       cout << "ID:" << u.id << " " << "Password: " << u.pwd << endl;
   }
}

>> std::skipws将前进到文件末尾或下一个名称,如您所希望的那样设置eof()标志。

行实际上并不存在于二进制文件中,除非是作为约定,就像一行是由换行符'n'终止的字节序列(与'''n'都不同)(该约定可能因操作系统而异,某些操作系统使用"rn""nr"甚至'r'作为行尾)。

您可以使用std::getline循环读取行,直到文件结束,将行保留在某个std::string中,并解析最后一行字符串(使用std::istringstream、sscanf等)

或者,如Tony D’answer所建议的,将行尾视为空格分隔符。然后,具有Tyler Bred John Doe单个行将被处理为两个行Tyler BredJohn Doe

一种可能是这样的:

void confirm(const char* f)
{
    user u;
    ifstream in(f, ios::in | ios::binary);
    while (in >> u.id >> u.pwd)
        ;
    cout << "Registration Complete.n";
    cout << "Your user information:n";
    cout << "ID:" << u.id << " " << "Password: " << u.pwd << "n";
}

然而,如果我这样做的话,我会为user结构定义一个operator>>,类似于以下内容:

struct user {
    std::string id;
    std::string pwd;
    friend std::istream &operator>>(std::istream &is, user &u) { 
        return is >> u.id >> u.pwd;
    }
};

这将主代码简化了一点,变成这样:

user u;
while (in >> u)
    ;

诚然,在这种情况下(只有两个字段),这只是一个微小的改进,但这仍然是一个小的改进,当你有更多的字段要处理时(尤其是当读取对象的代码变得更复杂时),将读取对象的码码隔离到自己的函数中可以大大简化代码的其余部分。

请注意,无论最后一个数据后面的文件中有多少空白,这都是有效的(尾随或缺少空白不会影响结果)。

实时演示

delete u

这将释放与user结构相关联的存储器。如果文件中有超过1行,您将获得访问冲突,因为在下一次迭代中,您将再次接触与user结构相关的内存。

还有这个:

in.open(f, ios::in | ios::binary);
    if (!in)
    {
        cout << "failed to read file!" << endl;
        exit(0);
    }

是测试文件是否成功打开的正确方法。此外,您还需要处理每次读取中的换行符。

我假设你在找到第一场比赛后想退出。你应该这样修改你的代码:

if((u1.id == u->id)&& (u1.pwd == u->pwd))
{
     cout << "Registration Complete." << endl;
     cout << "You registered;" << endl;
     cout << "ID:" << u1.id << "," << "Pwd: " << u1.pwd << endl;
     delete u;
     break;
}

尽管我不喜欢这种被调用者负责释放内存的模式。我建议让调用者决定如何处理user结构中传递的内存。作为一个额外的建议,使用调试器可能有助于诊断此类问题。