当前位置:C++技术网 > 资讯 > 我定义了一个类,先将其写入文件当中,这没什么问题,问题是读出文件是就停止工作了

我定义了一个类,先将其写入文件当中,这没什么问题,问题是读出文件是就停止工作了

更新时间:2017-04-09 00:32:59浏览次数:1+次

struct Node
{
string eng;
string chi;
Node *next;
};

class English
{
private:
Node *m_head;
int num;
public:
English():m_head(0),num(0){}
~English();
void add(string a,string b);
int get_amount();
string get_english();
string get_chinease();
friend ostream & operator<< (ostream &os,const English &st);

};

int a=0;

void add_word()
{
int i=0;
ofstream ofile("data.txt",ios::app);
ofstream ofile2;
string english,chinease;
system("cls");
    cin.get();
cout<<"请输入所要录入的英文:";
getline(cin,english).get();
cout<<"请输入所要录入的中文:";
getline(cin,chinease).get();
st.add(english,chinease);
ofile<<st;
ifstream ifile;
ifile.open("num.txt");
ifile>>i;
ifile.close();
a=i+1;
ofile2.open("num.txt");
ofile2<<a;
cout<<"录入成功!!!\n";
system("pause");
}

void eng_change_chi()
{
bool flag=true;
int m,i=5,j;
srand(time(0));
m=int(rand()%a);
English s;
FILE *fp;
fp=fopen("data.txt","rb");
for(j=0;j<m;j++)
fread(&s,sizeof(English),1,fp);
fclose(fp);
string e,f;
e=s.get_english();
cout<<"英文是:"<<e<<endl;
cout<<"请输入它的中文注释:";
cin.get();
getline(cin,f).get();
while(!(f==s.get_chinease()))
{
i--;
cout<<"错误,您还有 "<<i<<" 次机会"<<endl;
char ch;
cout<<"还想继续吗?(Y/y)"<<endl;
cin>>ch;
ch=toupper(ch);
if(ch!=''''Y'''')
{
flag=false;
break;
}
cout<<"英文是:"<<e<<endl;
   cout<<"请输入它的中文注释:";
   getline(cin,f).get();
if(i==0)
{
flag=false;
break;
}
}
if(flag)
{
cout<<"正确!!!\n";
cout<<s.get_english()<<"---"<<s.get_chinease()<<endl;
system("pause");
}
else
{
cout<<"错误!!!";
system("pause");
}
}


C++技术网会员解答:

    您好,感谢您对C++技术网的信任与支持。

    因为代码不是完整的,所以通过QQ,跟你进行了沟通,下面是沟通的记录:


读出来以后提示输入,当输入的中文与它的意思相同就是正确的,有5次机会。。。
C++技术网 2017/4/8 23:11:55
写入文件的是英文单词对吧
会员 2017/4/8 23:13:08
写入的是英文单词和中文意思,写入的是English这个类的对象。。。在addword那个函数中。。。
23:14:46
C++技术网 2017/4/8 23:14:46
那就是说,事先录入按照英文单词和中文一起输入,存储起来。
C++技术网 2017/4/8 23:15:15
num.txt
C++技术网 2017/4/8 23:15:19
这个是做什么的呢
23:17:22
会员 2017/4/8 23:17:22
是的,将他们存在文件中,而num.txt就是来存储我所输入的英文单词的个数,当进入english那个函数时,通过随机数使它随机从data.txt读出一个单词。。。我觉得就是那一块随机读出那一块有问题。。。
23:22:06
C++技术网 2017/4/8 23:22:06
提问的描述把我吓到了。
会员 2017/4/8 23:22:24
怎么了?/小纠结
C++技术网 2017/4/8 23:22:35
我以为你是要将类对象存入文件,然后读入内存后让对象跑起来,这个就复杂了。
C++技术网 2017/4/8 23:22:39
你这个好说
C++技术网 2017/4/8 23:22:52
不过你这个做法,效率比较低。
C++技术网 2017/4/8 23:23:15
我给你一些改进的建议,可以让这个功能流程更方便高效些
会员 2017/4/8 23:23:46
谢谢了。。。那个方法是我能想到的唯一方法。。。
C++技术网 2017/4/8 23:24:06
能这么想,已经不错了。都是慢慢摸索过来的。
23:24:19
C++技术网 2017/4/8 23:24:19
那我总结一下,看看理解的对不对
会员 2017/4/8 23:24:43
恩,谢谢
C++技术网 2017/4/8 23:25:45
你想将一系列的英语单词和中文词语成对的保存到文件。然后存入总对数。在读出文件时,随机取一对,然后显示英文单词,输入中文,如果正确就过关,如果不对,有5次机会。
C++技术网 2017/4/8 23:25:47
对吧。
会员 2017/4/8 23:26:10
是的,就是这个意思。。。。
23:26:34
C++技术网 2017/4/8 23:26:34
好的。
C++技术网 2017/4/8 23:27:29
那我给你提供一个整体的思路,和基本的代码实现。然后你在我的基础上改善代码,可以简化代码,这样说不定你之前的问题,也就不是问题了。
C++技术网 2017/4/8 23:28:16
现在就写解答了。下次贴代码,如果是长长的,就贴完整的代码,main都要在里面,我们可以直接运行查看问题。
会员 2017/4/8 23:28:56
好的太谢谢您了,感觉很麻烦你们啊。。。

     总结一下功能:将一系列的英语单词和中文词语成对的保存到文件。然后存入总对数。在读出文件时,随机取一对,然后显示英文单词,输入中文,如果正确就过关,如果不对,有5次机会。就是一个单词记忆软件。想法挺好的,挺实用的程序。

    st是全局的English对象。你的思路是将输入的所有的数据存储在对象中,而对象中数据是通过指针形式来管理的,也就是说,你需要动态分配内存来存储对象的数据。那么这个动态存储的数据和对象本身的内存是分开的。所以,如果你的思路是将对象存储起来,就认为是将数据存储起来,就有问题了。你存储对象并没有真正存储数据哦!!

    然后你不知道存储的单词对的数量,所以用num.txt来作为数量记录文件。这样的话,也就将数据文件data.txt和num.txt分开了。本来作为一个整体的数据,被分成两部分的话,容易出现不同步的问题。比如文件破坏或丢失或被篡改,都会导致另一个文件解析不正常。这是不推荐的做法。对于一个整体的数据,如果能够让数据和描述数据的信息存放在一个文件里,就存放在一个文件里,这样可以很好的保证文件数据的完整性。比如jpg图片格式,文件开头就是关于文件的格式信息,后面才是图像数据。你可以参考这样的做法来实现。而基于上面存储数据的方式,本身也有问题。所以我们将在后面详细分析一下思路,供你改进。

    一开始看到你描述,我就隐隐感觉到你直接使用了序列化的方式来做,通过进一步的看代码确认,果真如此。对象序列化和反序列化是将对象的二进制数据保存成文件,然后再将文件数据读入内存,并将二进制数据转化为对象来使用。

    但从对象序列化来讲,是没有问题的。那么你运行之后,出现“程序已经停止运行”是什么问题呢?前面已经说了,在存储数据的时候,你是动态分配内存。那么内存自然是在堆里分配的,和对象所在的内存没有直接关系,只是一个使用的关系,堆中的内存并不属于对象。所以在存储对象的时候,堆的数据是没有存储到文件的。存储到文件的只是对象中的指针而已。这样一来,当你再将文件数据读入,并存入一个对象的内存中,这样这个对象就变成了文件中存储的对象了。这样的话,对象就好像被人占用了身体,是可以正常执行的。这个过程就是对象序列化和反序列化的过程。序列化指的是二进制数据流的存储和反向解析为对象的流操作。

    对象能正常执行,没错。但是文件中存储的对象的数据在堆中。当你再次读取文件的时候,你并没有存储堆内存的地址,而且也不应该存储。毕竟文件保存后,如果程序退出,分配的堆也会被释放掉。尽管后续再用同样的地址访问堆,也是非法的,要么那块内存处于未分配状态,要么是分配给其他程序使用,你存储的对象有堆内存的起始地址,但是并没有使用权了,所以对这个内存的操作就是非法操作。这样的话,系统就会干掉非法访问内存的程序,也就出现了“程序已停止运行”的现象。


    整体的功能流程基本上没有大问题。那么基于上面的分析,我给你提供一些改进建议,供你参考。

1.可以将英文单词和中文单词用空格隔开,放在一行,一对占一行。那么在读取文件的时候,读取换行符的个数,就可以确定英文单词和中文单词的对数,而不需要单独存储一个文件了。这样是将单词对数隐含在格式里了。这个实现很方便。

2.如果你还需要存放其他信息,那么你可以参考一般的文件的格式做法,在开始的多少个字节,分别定义存放规定的数据,比如单词的对数,词组修改的时间,操作者,等等。然后在读取文件的时候,先解析前面若干个字节的数据,得到文件的相关信息。这个做法很普遍。

3.如果你想用直接序列化对象的方式存储数据,那么你存储的数据都必须在对象的范围内,这样就不要用指针形式动态分配。动态分配的内存不在对象空间,存储对象时就把数据丢失了,以后再访问还会崩溃。解决的办法就是使用静态数组。

4.一般常规存储数据都是用数据文件来存储,毕竟对象的相关信息和数据无关,这样也会增加很多的麻烦,比如对象一些信息并不是我们需要的,存入文件占用空间,效率变低。我们只需要数据文件,不需要对象的信息。如果直接将对象作为数据存储,今后如果要新增修改数据的格式,那么对象都需要做调整,牵连比较大。所以,我们要用文件来定义格式,直接存储数据。对象的序列化并不是用来存储数据场景使用的,而是用于对象的永久存储、网络传输等,之后再快速使用。

5.如果你确实想用对象序列化的方式存储,那么你一定要将链表的所有数据全部也要存储好,反序列化后要处理好指针的指向关系。不过就麻烦了。不推荐。

    相信解答到这里,你知道问题所在,也就好解决问题。用文件数据直接定义数据格式,就可以了。我们按照行来存储数据,一行行的将数据写入文件,存储,就挺好。

    你在这个基础上再改进吧。