数字在 C++ 中存储为特殊字符
Number getting stored as special character in C++
#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++ 和这个社区的新手,这是我一直在处理的代码,联系人的 phone 号码被保存为随机特殊字符。这是我认为出现问题的代码的一半 关于如何解决它的任何想法?这会很有帮助。谢谢!
我认为您希望在文本文件中看到 phone 数字,类似于“15551234567”。但是,long long
在内存中并不是以这种形式存储的。它实际上存储为 64 位二进制整数。您描述的特殊字符可能是该整数的编码版本。如果你读回数据,你应该会发现它仍然是一个整数。
但是,还有一个问题。您在 fstream open
命令中缺少 ios::binary
。每个 ios
标志都为流注入特定的行为:
ios::out
- 表示该流应该是一个输出流,您可以将字节写入
ios::app
- 指示此流应以“追加”模式打开。这意味着它不会在您每次打开文件时都清除文件的内容,并且输出到流的任何字节都会附加到文件的末尾。
ios::binary
- 以二进制模式打开文件,当您想要 input/output 二进制数据而不只是文本时需要这种模式。
您想用 ios::out | ios::app | ios::binary
打开文件。忘记二进制将导致非常难以调试错误。
现在二进制模式有点麻烦。抱歉,这篇文章读起来很长,但是如果您了解它背后的历史,就会更容易掌握这个标志。
早在计算的早期,就如何将换行符写入文件存在分歧。这样,在打字机时代,开始一条新线被分成两个动作。有一个“carriage return”将打字机的滑动位移回行首(这是运动的响亮部分),还有一个“换行”将纸张向上移动一个点。其中每一个都是单独的操作,因此在 ASCII 中为它们指定了单独的字符,这是将文本写入字节串的确定方法之一。 8 位数字 10 编码了一个换行符(又名 LF
),而 8 位数字 13 编码了一个回车符 return(又名 CR
)。这将允许一个人做一些事情,比如过度打字,这是一种技巧,其中一个人输入一个字符(如字母)然后返回在顶部添加另一个字符(如重音)。您可能会先键入 a
,然后输入 `
,然后输入 `
,就像您在打字机上所做的那样,从而编写 à
。
一些操作系统(例如 Windows)将下一行的开头编码为这两个字符,因此您会在文本文件中看到 CR LF
。其他操作系统(例如 Unix)认为不值得在每一行的末尾浪费一个宝贵的字节,因此他们选择只用 LF
来表示新行的开始。其他人(例如 Macintosh)决定将新行表示为 CR
。没人同意。
为了解决这个问题,许多文件 reading/writing API 会特殊处理这些字符。 fopen
和 fstream
遵循一种模式,如果他们在文本文件中看到 CR LF
或 CR
,他们在阅读时会默默地将其转换为 LF
字符.这使您可以读取每种文件类型。同样,如果它在写入时看到一个 LF
字符,它会将其扩展为平台指定的新行应该看起来的样子。这使您可以编写编写文本文件的跨平台代码,而不必注意每个平台上使用的换行符!
然而,这会给二进制数据带来巨大的问题。考虑写为 32 位数字的数字 302,844,416。在 hexadecimal 中,我们将其写为 0x120D0A00
(十六进制是编程中编写数字的一种流行方式,因为每个字节都可以写成十六进制的 2 个字符)。问题是数字的中间两个字节 0x0D 和 0x0A。在十进制中,它们是 13 和 10,您应该将它们识别为与 CR
和 LF
.
相同的字节
如果程序试图在“文本模式”下读取该数字,它将看到 CR LF
对,并根据 C 规则将其转换为单个 LF
。现在,我们的数字不是 0x120D0A00
,而是 0x120A00XX
,其中 XX
是文件中的下一个字节。很糟糕的事情!不仅此数据已损坏,而且您可能需要下一个字节来存放文件中的下一个字节!
ios::binary
和 fopen
的“b”标志解决了这个问题。他们告诉 C/C++ 数据将是二进制的。不会有任何新行要转换。如果您将字节写入二进制流,它们将直接写入文件,而无需任何聪明的尝试来处理新行。
您的 phone 号码存储为 long long
,这是一种二进制整数格式。如果没有 ios::binary
,您 运行 数字的风险恰好 [=72=] 恰好 有一个 CR LF
对,并且 fstream
会损坏你的数据。 ios::binary
告诉 fstream
不要以那种方式弄乱数据。
#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++ 和这个社区的新手,这是我一直在处理的代码,联系人的 phone 号码被保存为随机特殊字符。这是我认为出现问题的代码的一半 关于如何解决它的任何想法?这会很有帮助。谢谢!
我认为您希望在文本文件中看到 phone 数字,类似于“15551234567”。但是,long long
在内存中并不是以这种形式存储的。它实际上存储为 64 位二进制整数。您描述的特殊字符可能是该整数的编码版本。如果你读回数据,你应该会发现它仍然是一个整数。
但是,还有一个问题。您在 fstream open
命令中缺少 ios::binary
。每个 ios
标志都为流注入特定的行为:
ios::out
- 表示该流应该是一个输出流,您可以将字节写入ios::app
- 指示此流应以“追加”模式打开。这意味着它不会在您每次打开文件时都清除文件的内容,并且输出到流的任何字节都会附加到文件的末尾。ios::binary
- 以二进制模式打开文件,当您想要 input/output 二进制数据而不只是文本时需要这种模式。
您想用 ios::out | ios::app | ios::binary
打开文件。忘记二进制将导致非常难以调试错误。
现在二进制模式有点麻烦。抱歉,这篇文章读起来很长,但是如果您了解它背后的历史,就会更容易掌握这个标志。
早在计算的早期,就如何将换行符写入文件存在分歧。这样,在打字机时代,开始一条新线被分成两个动作。有一个“carriage return”将打字机的滑动位移回行首(这是运动的响亮部分),还有一个“换行”将纸张向上移动一个点。其中每一个都是单独的操作,因此在 ASCII 中为它们指定了单独的字符,这是将文本写入字节串的确定方法之一。 8 位数字 10 编码了一个换行符(又名 LF
),而 8 位数字 13 编码了一个回车符 return(又名 CR
)。这将允许一个人做一些事情,比如过度打字,这是一种技巧,其中一个人输入一个字符(如字母)然后返回在顶部添加另一个字符(如重音)。您可能会先键入 a
,然后输入 `
,然后输入 `
,就像您在打字机上所做的那样,从而编写 à
。
一些操作系统(例如 Windows)将下一行的开头编码为这两个字符,因此您会在文本文件中看到 CR LF
。其他操作系统(例如 Unix)认为不值得在每一行的末尾浪费一个宝贵的字节,因此他们选择只用 LF
来表示新行的开始。其他人(例如 Macintosh)决定将新行表示为 CR
。没人同意。
为了解决这个问题,许多文件 reading/writing API 会特殊处理这些字符。 fopen
和 fstream
遵循一种模式,如果他们在文本文件中看到 CR LF
或 CR
,他们在阅读时会默默地将其转换为 LF
字符.这使您可以读取每种文件类型。同样,如果它在写入时看到一个 LF
字符,它会将其扩展为平台指定的新行应该看起来的样子。这使您可以编写编写文本文件的跨平台代码,而不必注意每个平台上使用的换行符!
然而,这会给二进制数据带来巨大的问题。考虑写为 32 位数字的数字 302,844,416。在 hexadecimal 中,我们将其写为 0x120D0A00
(十六进制是编程中编写数字的一种流行方式,因为每个字节都可以写成十六进制的 2 个字符)。问题是数字的中间两个字节 0x0D 和 0x0A。在十进制中,它们是 13 和 10,您应该将它们识别为与 CR
和 LF
.
如果程序试图在“文本模式”下读取该数字,它将看到 CR LF
对,并根据 C 规则将其转换为单个 LF
。现在,我们的数字不是 0x120D0A00
,而是 0x120A00XX
,其中 XX
是文件中的下一个字节。很糟糕的事情!不仅此数据已损坏,而且您可能需要下一个字节来存放文件中的下一个字节!
ios::binary
和 fopen
的“b”标志解决了这个问题。他们告诉 C/C++ 数据将是二进制的。不会有任何新行要转换。如果您将字节写入二进制流,它们将直接写入文件,而无需任何聪明的尝试来处理新行。
您的 phone 号码存储为 long long
,这是一种二进制整数格式。如果没有 ios::binary
,您 运行 数字的风险恰好 [=72=] 恰好 有一个 CR LF
对,并且 fstream
会损坏你的数据。 ios::binary
告诉 fstream
不要以那种方式弄乱数据。