数字在 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 会特殊处理这些字符。 fopenfstream 遵循一种模式,如果他们在文本文件中看到 CR LFCR,他们在阅读时会默默地将其转换为 LF 字符.这使您可以读取每种文件类型。同样,如果它在写入时看到一个 LF 字符,它会将其扩展为平台指定的新行应该看起来的样子。这使您可以编写编写文本文件的跨平台代码,而不必注意每个平台上使用的换行符!

然而,这会给二进制数据带来巨大的问题。考虑写为 32 位数字的数字 302,844,416。在 hexadecimal 中,我们将其写为 0x120D0A00(十六进制是编程中编写数字的一种流行方式,因为每个字节都可以写成十六进制的 2 个字符)。问题是数字的中间两个字节 0x0D 和 0x0A。在十进制中,它们是 13 和 10,您应该将它们识别为与 CRLF.

相同的字节

如果程序试图在“文本模式”下读取该数字,它将看到 CR LF 对,并根据 C 规则将其转换为单个 LF。现在,我们的数字不是 0x120D0A00,而是 0x120A00XX,其中 XX 是文件中的下一个字节。很糟糕的事情!不仅此数据已损坏,而且您可能需要下一个字节来存放文件中的下一个字节!

ios::binaryfopen 的“b”标志解决了这个问题。他们告诉 C/C++ 数据将是二进制的。不会有任何新行要转换。如果您将字节写入二进制流,它们将直接写入文件,而无需任何聪明的尝试来处理新行。

您的 phone 号码存储为 long long,这是一种二进制整数格式。如果没有 ios::binary,您 运行 数字的风险恰好 [=7​​2=] 恰好 有一个 CR LF 对,并且 fstream 会损坏你的数据。 ios::binary 告诉 fstream 不要以那种方式弄乱数据。