在C++中读写二进制文件

3
我完全是新手,只自学了几个小时的C++,昨天才开始。SO:
我有一个未压缩的简单的beep.wav文件,只有大约3秒钟,并带有单个的哔哔声。
我的最终目标是,只需读取文件,同时写入二进制数据,包括:头文件,ftm和数据或所有可读取十六进制数据,并将其转储到简单的beep.txt文件中。
请问,在C++中是否可能实现这一目标?如果可以,如何开始读取和转储二进制文件?
我可以使用<fstream>类读取文件。
#include <iostream>
#include <fstream>

   int main()
   {
       ifstream myfile ("beep.wav", ios::in|ios::binary|ios::ate);
       if (myfile.is_open())
       {
        //reading goes here
        }
       return 0;
    }

1
你想要实现什么目标?为什么要将二进制数据写入文本文件?你期望文本文件会有哪些二进制数据所没有的内容? - xxbbcc
2
首先,就功能而言,计算机可以做的所有事情在C++中都是可能的(与大多数其他通用编程语言一样)。世界运行在C和C++上,所以它是可以完成的。然而,你到底想要做什么是完全不清楚的。你想将一个文件的内容复制到另一个文件吗?你想解析WAV头和PCM数据并将它们转换为文本吗?你甚至知道未压缩(也称为PCM)WAV文件的结构吗? - yzt
1
@xxbbcc 嗯,我正在学习C++来创建一个声音处理工具,并且为了从0开始理解它的所有工作原理,我需要学习/实验二进制数据存储的工作方式以及如何操作它。 - blue thief
@yzt 就像我之前说的,我一直在学习声音、音频信号和音频驱动程序,这个主题是我理解工作原理的途径。所以,我只是在尝试玩弄它...如果你愿意的话。 - blue thief
@bluethief 如果是这样,你首先需要学习二进制文件和文本文件之间的区别。它们存在的原因是有意义的,不能直接将一个文件的内容写入另一个文件而不产生后果。你可以读取 WAV 文件的内容,但要以有意义的方式将其写入文本文件,你必须翻译数据。如何做取决于你想要什么样的文本输出。 - xxbbcc
@xxbbcc 谢谢,我非常喜欢你的建议。一旦我摆弄好这个东西,我就会创建一个简单的频谱图。我想要构建的应用程序将基于频谱图。如果您有任何进一步帮助我的建议,我很乐意听取。 - blue thief
4个回答

3

yzt所提到的,你需要事先知道你的.wav文件中有什么,以及它们的顺序。据我所知,你将拥有标签、标题和值(样本)以及压缩信息。因此,为了开始:

如果你知道,例如,第一件要做的事情是读取压缩率,那么你会通过提取可能的double来启动你的读取过程:

ifstream myfile("beep.wav", ios::in|ios::binary);

double compression_rate;
myfile.read((char*)&compression_rate, sizeof(compression_rate));

// As well as probably the sample rate...
double sample_rate;
myfile.read((char*)&sample_rate, sizeof(sample_rate));

也许,样本数量会有所增加:
int nb_samples;
myfile.read((char*)&nb_samples, sizeof(nb_samples));

然后,这些样本的值...(在这里存储为double类型的vector

vector<double> vect;
vect.resize(nb_samples);
myfile.read((char*)&vect[0], nb_samples * sizeof(double));

再说一遍,你打开的.wav文件是由什么组成的?

一旦你完全掌握了内容,就可以开始从头开始编写你自己的.wav文件...


入门 - 还有这里

字节 - PCM Wav文件的“尸检”


好的,谢谢,但我在这里有点迷茫,因为这些新函数。我也不能说太多关于 .wav 文件的类型,因为它是未压缩的纯音频 wav 文件。 - blue thief
这些是从 ifstream 读取二进制数据的方法。当您读取一个值(比如一个 double)时,将其存储在一个变量中。结果 compression_rate 获得了它的值,并且文件光标的位置移动了一个 double 的大小。然后,您准备读取下一个信息(这里是 int)。光标再次移动。然后,您从当前位置开始读取 nb_samplesdouble(每次读取一个值时,光标都会移动)... 直到读取结束(EOF)。这对您有更多意义吗? - Gauthier Boaglio
谢谢。我现在比以前有更好的了解。但是这些链接真是太棒了。 - blue thief
很高兴能帮忙!第二个链接要求您熟悉(如果还不熟悉的话)C类型的大小(以字节为单位)read函数只是简单地读取一定量的字节。这就是该函数接受的第二个参数的目的,也是位于网页中间的表格的size列的目的。祝您在“声音处理工具”项目中好运... - Gauthier Boaglio
谢谢。我边学边做。声音处理工具只是一个频谱图。只是非常简单的一个。我正在学习这些,希望能够在本周内创建一个。 - blue thief
我非常有兴趣看到它的结果。如果你在得到结果后能分享一些东西,那就太棒了。 - Gauthier Boaglio

2

此示例将从输入文件读取数据,并将其作为两个字符的十六进制值(文本)写入。

ofstream outfile("beep.txt");
outfile << hex << setfill('0');

char c;
while( outfile.good() && myfile.get(c) )
{
    outfile << setw(2) << +(unsigned char)c;
}

谢谢Paddy,我尝试将您的代码集成到我的代码中,如前所述,但是我遇到了错误。更加简洁地说,“ifsteam未在此范围内声明”是什么意思?在添加您的代码之前,它运行良好.. 奇怪.. - blue thief
2
仔细阅读错误信息... 它试图告诉您没有名为 ifsteam 的东西(检查拼写)。 - paddy
1
@bluethief:很明显你需要一本好书,可以方便地参考和/或熟悉C++互联网参考,它不仅仅是一个问答论坛。 - ChiefTwoPencils

2

WAV文件格式规范告诉您WAV文件的二进制布局 - 您需要了解这一点,因为这告诉您按什么顺序读取哪些字节。(除了字节外,您还需要读取int和其他各种字段。) 读取此信息后,您可以将其翻译为显示的内容并写入文本文件。例如,您可以通过读取标头(我不知道确切的布局)并计算出对应的秒数来写出WAV文件中声音的长度(持续时间)。

从技术角度来看,您问题的所有其他答案都很好 - 它们向您提供有关如何在C ++中读/写二进制和文本数据的指针。


是的,这就是全部,这全都是规格的事情。 - Gauthier Boaglio
@bluethief 这个链接提供了一个相当不错的想法,告诉你在读取原始 PCM WAV 文件时应该按什么顺序读取字节:https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ - Gauthier Boaglio

1
你可以将整个文件读入缓冲区:
  1. 定位到文件末尾
  2. 创建一个足够大的缓冲区来存储文件
  3. 回到文件开头
  4. 将整个文件读入缓冲区

:

myfile.seekg(0, std::ios::end);
std::string buffer(myfile.tellg(), '\0');
myfile.seekg(0);

myfile.read(&buffer.front(), buffer.size());

谢谢:那么我如何以可观察的方式显示原始数据? - blue thief

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接