为每天进步一点点而努力。
C++是计算机视觉的重要的一部分,虽然在初始学习时Python应用比较广,但是大多数公司还是C++做计算机视觉类的比较多,因为C++可加密,所以我们来一起探索吧!看了这系列博客,将会学到C++的基本操作!(如果不敲代码,可能会一知半解)
第十一天 - 221009
目录
12.1.文本文件(text file)和二进制文件(binary file)
chapter 12 文件输入输出
12.1.文本文件(text file)和二进制文件(binary file)
文本文件是以 ASCII 字符形式存储数据的文件,通常用于存储字符流。文本文件按行组织,每行以换行符 ('\n') 结尾。源代码文件本身就是文本文件。
二进制文件是将数据存储在文件中的方式与存储在主存储器中进行处理的方式相同。它以二进制格式而不是 ASCII 字符存储。它通常用于存储数字信息(int、float、double)。通常二进制文件只能在程序中创建,其内容只能由程序读取。
以下是文本文件和二进制文件之间的一些区别。
- ASCII : 是American Standard Code for Information Interchange的缩写,是一种用于电子通信的字符编码标准。详情请参考【1】
12.2.文件输入输出
图【3】
ifstream 类
- ifstream是输入文件流,即用于从文件读取输入的数据流。
- 因为 ifstream 是 istream,所以您可以对 istream 执行的任何操作也可以对 ifstream 执行相同的操作。
- 特别是,cin 是 istream 的一个示例,因此您可以使用 cin 执行的任何操作也可以使用任何 ifstream 执行。
- 使用 ifstreams (和 ofstreams )需要包含 fstream 标头:
#include <fstream>
- 但是,在使用 ifstream 之前,您必须创建一个 ifstream 类型的变量并将其连接到特定的输入文件。
- 在使用新打开的文件之前,应始终检查以确保文件正确打开。每个流对象都有一个 fail() 方法,如果流处于失败状态则返回“true”,否则返回“false”
- 一旦创建了 ifstream 并将其连接到打开的文件,就可以像从 cin 中读取数据一样从文件中读取数据
- 完全使用流后,应始终关闭它以防止可能的损坏。
ofstream 类
- ofstream是一个输出文件流,其工作方式与 ifstreams 完全相同,只是输出而不是输入。
- 一旦创建、打开并检查了一个 ofstream 没有失败,就可以像 cout 一样使用它
读取数据文件
读取输入数据文件时的关键问题之一是知道要读取多少数据,以及何时停止读取数据。有三种常用的技术,如下所示。(从键盘阅读时也可以使用。)
- 指定记录数
int nData;
double x, y, z;
fin >> nData;
for( int i = 0; i < nData; i++ ) {
fin >> x >> y >> z;
// Do something with x, y, z
} for loop reading input data
- 寻找特殊的值(组合)作为触发停止读取数据
double x, y;
while( true ) {
fin >> x >> y;
if( x = = 0.0 && y = = 0.0 )
break;
// Do something with x and y
} // while loop reading input data
- 检测文件结尾
double x, y, z;
fin >> x >> y >> z;
while ( !fin.eof( ) ) {
// Do something with x, y, z
// Read in the new data for the next loop iteration.
fin >> x >> y >> z;
} // while loop reading until the end of file
使用流状态进行错误检查
- 每个流都有四个位来跟踪流的当前状态:
- 当尝试读取文件末尾之后,eofbit设置为 true。
- 当读取损坏的数据时设置坏位,即当文件中的数据类型与正在读取的类型不匹配时。
- 当文件无法打开、读取文件末尾或读取损坏的数据时,会设置故障位。
- 当其他三位全部为假时, goodbit设置为 true,否则为 false。
- 以下方法用于检查和重置位:
- eof( ) - 返回 eof 位的状态。
- bad( ) - 返回坏位的状态。
- fail( ) - 返回失败位的状态。
- good( ) - 返回好位的状态。
- clear( ) - 设置好位为真,所有其他位为假。如果在输入错误名称后要求用户输入新文件名,或者在遇到前一个文件的结尾后为新文件重新使用流变量时,需要重置状态。
- (高级)有趣的是,数据类型“流”到“布尔”的自动转换非常有用。
- 转换产生的布尔值就是好位的值。(如果“fin”是一个ifstream,那么测试“while(fin)”就等价于“while(fin.good())”所以如果一个流对象被解释为一个布尔值,那么如果流是好的则为真,否则为假。)
- 在代码中,请注意 >> 运算符的“值”是流本身,然后将其解释为 true 或 false。
12.3.文件和文件模式(file mode)
C++ 语言允许我们执行重要的磁盘输入/输出操作,例如 -
- 在磁盘上创建一个新文件。
- 读取存储在磁盘上的文件。
- 将数据写入存储在磁盘上的文件。
- 将新数据附加到存储在磁盘上的文件的末尾。
- 修改存储在磁盘上的文件的内容。
为了执行任何文件操作,C++ 为我们提供了一些文件流类,例如 -
- ifstream,执行文件输入操作。
- ofstream,执行文件输出操作。
- fstream,执行任何文件输入和输出操作。
打开文件
通过使用任何文件流类的对象,我们可以调用 open()函数,使用它我们可以提供存储在磁盘上的文件的位置,并且作为回报(如果找到文件),open()函数打开特定模式下的文件,让您执行特定的文件操作。
file-stream-object("filename", mode);
- file-stream-object,是用于执行特定文件操作的文件流类的一个。
- filename,是我们要对其执行文件操作的文件的名称。
- mode,是我们要打开文件的单个或多个文件模式。
文件模式
在 C++ 中,对于每个文件操作,都存在一个特定的文件模式。这些文件模式允许我们创建、读取、写入、附加或修改文件。文件模式在ios类中定义。让我们看看可以在磁盘上打开文件 的所有这些不同模式。【4】
12.4.读取
使用getline()函数读取文件
- 新建一个file.txt,内容为
#include<iostream>
#include<fstream>
using namespace std;
int main() {
int size = 40;
char arr[size];
ifstream fin;
fin.open("file.txt", ios::in);
while (fin) {
fin.getline(arr, size);
cout << arr;
cout << '\n';
}
fin.close();
}
12.5.流状态(stream state)和输入验证
流状态
ios_base 类包含几个状态标志【5】
ios 还提供了一些成员函数以便方便地访问这些状态:
最常处理的位是failbit,它在用户输入无效输入时设置。
#include<iostream>
#include<fstream>
using namespace std;
int main()
{
cout << "Enter your age: ";
int age{};
cin >> age;
}
注意,该程序希望用户输入一个整数。但是,如果用户输入非数字数据,例如“Alex”,cin 将无法提取任何要老化的数据,并且会设置故障位。
如果发生错误并且流设置为 goodbit 以外的任何值,则该流上的进一步流操作将被忽略。这种情况可以通过调用 clear() 函数来清除。
输入验证
输入验证是检查用户输入是否符合某些标准的过程。输入验证通常可以分为两种类型:字符串和数字。
通过字符串验证,我们将所有用户输入作为字符串接受,然后根据其格式是否正确接受或拒绝该字符串。例如,如果我们要求用户输入电话号码,我们可能希望确保他们输入的数据有十位数字。在大多数语言(尤其是 Perl 和 PHP 等脚本语言)中,这是通过正则表达式完成的。C++ 标准库也有一个正则表达式库。由于正则表达式与手动字符串验证相比速度较慢,因此只有在不关心性能(编译时和运行时)或手动验证过于繁琐时才应使用它们。
对于数值验证,我们通常关心确保用户输入的数字在特定范围内(例如,0 到 20 之间)。然而,与字符串验证不同的是,用户可以输入根本不是数字的东西——我们也需要处理这些情况。
为了帮助我们,C++ 提供了许多有用的函数,我们可以使用它们来确定特定字符是数字还是字母。以下函数位于 cctype 标头中:
字符串验证
让我们通过要求用户输入他们的姓名来做一个简单的字符串验证案例。我们的验证标准将是用户只输入字母字符或空格。如果遇到任何其他情况,输入将被拒绝。
对于可变长度输入,验证字符串的最佳方法(除了使用正则表达式库之外)是逐步检查字符串的每个字符并确保它符合验证标准。这正是我们将在这里做的,或者更好的是,这就是我们要做的std::all_of
数值验证
在处理数字输入时,显而易见的方法是使用提取运算符将输入提取为数字类型。通过检查故障位,我们可以判断用户是否输入了数字。
#include <iostream>
#include <limits>
int main()
{
int age{};
while (true)
{
std::cout << "Enter your age: ";
std::cin >> age;
if (std::cin.fail()) // no extraction took place
{
std::cin.clear(); // reset the state bits back to goodbit so we can use ignore()
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // clear out the bad input from the stream
continue; // try again
}
if (age <= 0) // make sure age is positive
continue;
break;
}
std::cout << "You entered: " << age << '\n';
}
如果用户输入一个整数,则提取将成功。std::cin.fail() 将评估为假,跳过条件,并且(假设用户输入一个正数),我们将点击 break 语句,退出循环。
如果用户输入以字母开头的输入,则提取将失败。std::cin.fail() 将评估为真,我们将进入条件。在条件块的末尾,我们会点击 continue 语句,它会跳回到 while 循环的顶部,并要求用户再次输入输入。
还有一种情况,那就是用户输入一个以数字开头但包含字母的字符串(例如“34abcd56”)。在这种情况下,起始数字 (34) 将被提取到年龄中,字符串的其余部分 (“abcd56”) 将留在输入流中,并且不会设置故障位。这会导致两个潜在问题:
- 如果希望这是有效的输入,那么您现在的流中有垃圾。
- 如果不希望这是有效的输入,它不会被拒绝(并且您的流中有垃圾)。
让我们解决第一个问题。这很简单:
#include <iostream>
#include <limits>
int main()
{
int age{};
while (true)
{
std::cout << "Enter your age: ";
std::cin >> age;
if (std::cin.fail()) // no extraction took place
{
std::cin.clear(); // reset the state bits back to goodbit so we can use ignore()
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // clear out the bad input from the stream
continue; // try again
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // clear out any additional input from the stream
if (age <= 0) // make sure age is positive
continue;
break;
}
std::cout << "You entered: " << age << '\n';
}
作为字符串的数字验证
上面的例子只是为了得到一个简单的值而做了大量的工作!处理数字输入的另一种方法是将其作为字符串读入,然后尝试将其转换为数字类型。
12.6.文件指针和随机访问
每个文件都维护两个指针,称为 get_pointer(在输入模式文件中)和 put_pointer(在输出模式文件中),它们告诉文件中将发生读取或写入的当前位置。(此上下文中的文件指针与 C++ 指针不同,但它的作用类似于书中的书签。)。这些指针有助于实现文件中的随机访问。这意味着直接移动到文件中的任何位置,而不是按顺序移动。
在某些情况下,随机访问可能是最佳选择。例如,如果您必须修改第 21 号记录中的值,则使用随机访问技术,您可以将文件指针放在第 21 号记录的开头,然后直接处理该记录。如果使用顺序访问,那么您必须不必要地遍历前 20 条记录才能到达第 21 条记录。
seekg()、seekp()、tellg() 和 tellp() 函数
在 C++ 中,随机访问是通过操作 seekg()、seekp()、tellg() 和 tellp() 函数来实现的。seekg() 和tellg() 函数允许您设置和检查get_pointer,seekp() 和tellp() 函数对put_pointer 执行这些操作。
seekg() 和tellg() 函数用于输入流(ifstream),seekp() 和tellp() 函数用于输出流(ofstream)。但是,如果将它们与 fstream 对象一起使用,则 tellg() 和 tellp() 返回相同的值。seekg() 和 seekp() 在 fstream 对象中的工作方式也相同。这些函数最常见的形式是:
seekg() & seekp() 和tellg() & tellp() 的工作原理是一样的,除了seekg() 和tellg() 对ifstream 对象起作用,而seekp() 和tellp() 对ofstream 对象起作用。在上表中,seek_dir 采用定义 enum seek_dir { beg, cur, end};。
seekg() 或 seekp() 在根据表格 1 使用时,会将 get_pointer 或 put_pointer 移动到绝对位置。
疑问与解答
1.图片进入电脑后是转换为二进制嘛?
现代和传统数字电子产品的所有部分都是二进制的。也就是说,计算机及其外围设备中的所有数据和几乎所有组件在其基本形式上都是二进制的。只有本质上明显是模拟的东西,例如声卡,才不是数字的(因此不是二进制的)。然而,此类设备的大部分组成是模拟和数字(二进制)域之间的转换。
因此,话虽如此,存储在数字媒体上的图像是以二进制作为基础格式存储的。正如其他人所说,可以将最原始的元素(二进制数字或bit )组合在一起,使它们几乎可以表示任何东西。图片元素(像素)的矩阵,每一个都是位的组合,可以用作使用数字电子设备对图像进行编码,并且经常这样做。【2】
参考文献
【1】https://en.wikipedia.org/wiki/ASCII
【2】https://www.quora.com/Is-it-true-that-binary-code-makes-up-images
【3】 CPlus Course Notes - File I/O
【4】C++ File and File Modes - Decodejava.com