问题描述
#include<fstream>
#include<string.h>
#include<iostream>
using namespace std;
class contact
{
long long ph;
unsigned char name[20],add[50],email[30];
public:
void create_contact()
{
cout<<"Phone: ";
cin>>ph;
cout<<"Name: ";
cin.ignore();
cin>>name;
cout<<"Address: ";
cin.ignore();
cin>>add;
cout<<"Email address: ";
cin.ignore();
cin>>email;
cout<<"\n";
}
void show_contact()
{
cout<<endl<<"Phone Number: "<<ph;
cout<<endl<<"Name: "<<name;
cout<<endl<<"Address: "<<add;
cout<<endl<<"Email Address : "<<email;
}
long long getPhone()
{
return ph;
}
unsigned char* getName()
{
return name;
}
unsigned char* getAddress()
{
return add;
}
unsigned char* getEmail()
{
return email;
}
};
fstream fp;
contact cont;
void save_contact()
{
fp.open("contactBook.txt",ios::out|ios::app);
cont.create_contact();
fp.write((char*)&cont,sizeof(contact));
fp.close();
cout<<endl<<endl<<"Contact Has Been Sucessfully Created...";
getchar();
嘿,我是 C++ 以及这个社区的新手,这是我一直在编写的代码,联系人的电话号码被保存为随机的特殊字符。这是我认为出现问题的代码的一半 关于如何修复它的任何想法?这会有很大帮助。谢谢!
解决方法
我认为您希望在文本文件中看到的电话号码类似于“15551234567”。但是,long long
不会以这种形式存储在内存中。它实际上存储为 64 位二进制整数。您描述的特殊字符可能是该整数的编码版本。再读回数据,应该会发现还是一个整数。
但是,还有一个问题。您在 fstream ios::binary
命令中缺少 open
。每个 ios
标志都为流注入了特定的行为:
-
ios::out
- 表示这个流应该是一个可以写入字节的输出流 -
ios::app
- 表示这个流应该以“追加”模式打开。这意味着每次打开文件时它都不会擦除文件的内容,并且输出到流的任何字节都会附加到文件的末尾。 -
ios::binary
- 以二进制模式打开文件,当您想要输入/输出二进制数据而不仅仅是文本时需要这种模式。
您想用 ios::out | ios::app | ios::binary
打开文件。忘记二进制将导致非常难以调试的错误。
现在二进制模式有点害人。抱歉,阅读时间过长,但如果您了解其背后的历史,则更容易掌握此标志。
早在计算的早期,关于如何将换行符写入文件存在分歧。这样,打字机时代开始新的一行就被分成了两个动作。有“回车”将打字机的滑动位移回行首(这是运动的响亮部分),还有“换行”将纸张向上移动一个位置。其中每一个都是单独的操作,因此它们在 ASCII 中被赋予了单独的字符,这是将文本写入字节字符串的权威方法之一。 8 位数字 10 编码换行(又名 LF
),8 位数字 13 编码回车(又名 CR
)。这将允许人们做诸如过度输入之类的事情,这是一种输入一个字符(如字母)然后返回在顶部添加另一个字符(如重音)的技巧。您可以先输入 à
,然后执行“回车”然后输入 a
,就像在打字机上所做的那样。
某些操作系统(例如 Windows)将下一行的开头编码为这两个字符,因此您会在文本文件中看到 `
。其他操作系统(例如 Unix)认为在每一行的末尾浪费一个宝贵的字节是不值得的,所以他们选择只用 CR LF
来表示新行的开始。其他人(例如 Macintosh)决定将新行表示为 LF
。没有人会同意。
为了解决这个问题,许多文件读/写 API 会特别对待这些字符。 CR
和 fopen
遵循一种模式,如果他们在文本文件中看到 fstream
或 CR LF
,他们会在阅读时默默地将其转换为 CR
字符.这使您可以读取每种文件类型。同样,如果它在写入时看到 LF
字符,它会将其扩展为平台指定的新行应该是什么样子。这使您可以编写跨平台代码来编写文本文件,而不必注意每个平台上使用的是哪个换行符!
然而,这会给二进制数据带来巨大的问题。考虑写为 32 位数字的数字 302,844,416。在 hexadecimal 中,我们将其写为 LF
(十六进制是编程中写数字的一种流行方式,因为每个字节都可以写为 2 个十六进制字符)。问题是数字的中间两个字节 0x0D 和 0x0A。在十进制中,它们是 13 和 10,您应该将它们识别为与 0x120D0A00
和 CR
相同的字节。
如果程序尝试以“文本模式”读取该数字,它将看到 LF
对,并将其转换为单个 CR LF
,根据 C 规则。现在,我们的数字不是 LF
,而是 0x120D0A00
,其中 0x120A00XX
是文件中的下一个字节。非常糟糕的事情!不仅这些数据已损坏,而且您可能需要文件中的下一个字节!
XX
和 ios::binary
的“b”标志解决了这个问题。他们告诉 C/C++ 数据将是二进制的。不会有任何要转换的新行。如果您将字节写入二进制流,它们会直接写入文件,无需任何巧妙的尝试来处理新行。
您的电话号码存储为 fopen
,这是一种二进制整数格式。如果没有 long long
,您可能会面临数字只是发生在其中包含 ios::binary
对的风险,而 CR LF
会破坏您的数据。 fstream
告诉 ios::binary
不要以这种方式弄乱数据。