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

时间:2016-01-06 05:27:35

标签: c++ file-io binary

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

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;
    }
}

不幸的是,这不起作用。我有“访问冲突...”错误信息。

我尝试了许多其他代码来解决问题,但这些事情也没有用。我认为问题是:if(in.eof()) 但我不知道如何改变它来解决问题。 如果有人指出我的错误或给我一个提示,我会非常感激。

4 个答案:

答案 0 :(得分:1)

在文件的每一行末尾通常都有换行符(或某些操作系统上的字符***)。你的代码......

ArrayList<String> listcountry_id,listcountry;

listcountry_id=new ArrayList<String>();
listcountry=new ArrayList<String>();

 try {  
        JSONArray jarr=new JSONArray("your responce data here");

        for(int i=0;i<jarr.length();i++){

            JSONObject jobj=jarr.getJSONObject(i);
            listcountry_id.add(jobj.getString("country_id"));
            listcountry.add(jobj.getString("country"));              
        }       
    BindSpinnerData();      
    } catch (JSONException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }       
}

 spnData.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
            // TODO Auto-generated method stub
            String ids = String.valueOf(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> arg0) {
            // TODO Auto-generated method stub

        }
    });

private void BindSpinnerData() {
    // TODO Auto-generated method stub
    ArrayAdapter<String> spinnerLocation = new ArrayAdapter<String>(
            getApplicationContext(), R.layout.spinneritem, listcountry);
    spinnerLocation.setDropDownViewResource(R.layout.spinnerpopup);
    spnData.setAdapter(spinnerLocation); 
} 

假设您在阅读完最后一个人的姓名后点击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标记。

答案 1 :(得分:1)

一种可能性是这样的:

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";
}

但是,如果我这样做,我会为operator>>结构定义user,如下所示:

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)
    ;

不可否认,在这种情况下(只有两个字段)只是一个小小的改进,但它仍然是一个很小的改进,当你有更多的字段需要处理时(特别是当代码读取时)对象变得更复杂)隔离代码以将对象读入其自己的函数可以简化代码的其余部分。

请注意,无论在最后一个数据之后文件中有多少或者很少的空白区域(尾随\ n或缺少空格都不会影响结果),这都可以正常工作。

Live Demo

答案 2 :(得分:1)

除了作为约定之外,二进制文件中不存在行,就像一行是一个字节序列(所有不同于'\0'和来自'\n')终止通过换行符'\n'(该约定可能因操作系统与下一个操作系统而异,某些操作系统使用"\r\n""\n\r"甚至'\r'作为行结尾。)< / p>

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

或者,将行尾视为空格分隔符,如Tony D'answer所示。然后,带有Tyler Bred John Doe行将被处理为两行Tyler BredJohn Doe

答案 3 :(得分:0)

delete u

这将释放与user结构相关的内存。如果文件中有多行,则会出现访问冲突,因为在下一次迭代中,您将再次触摸与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结构中传递的内存。 作为使用调试器的附加建议可以帮助您诊断此类问题。