崔毅东 C++程序设计入门(下) 第7单元:出入虽同趣,所向各有宜– 文件输入输出 笔记

在这里插入图片描述在这里插入图片描述在这里插入图片描述
u07s01 - 关于缓冲IO
请你查查资料,说一说C/C++中带缓冲的IO与不带缓冲的IO的区别
答:所谓的不带缓冲的I/O是指进程不提供缓冲功能(但内核还是提供缓冲的)。不带缓冲,并不是指内核不提供缓冲,而是只单纯的系统调用,不是函数库的调用。系统内核对磁盘的读写都会提供一个块缓冲(在有些地方也被称为内核高速缓存),当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓冲进行排队,当块缓冲达到一定的量时,才会把数据写入磁盘。

带缓冲的I/O是指进程对输入输出流进行了改进,提供了一个流缓冲,当用fwrite函数网磁盘写数据时,先把数据写入流缓冲区中,当达到一定条件,比如流缓冲区满了,或刷新流缓冲,这时候才会把数据一次送往内核提供的块缓冲,再经块缓冲写入磁盘。(双重缓冲)。

无缓存IO操作数据流向路径:数据——内核缓存区——磁盘。
标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘。

(by 753215871)
u07s01 - C语言中的文件指针到底是个什么东西?
我们知道,C/C++中的指针是一个内存的地址。那么:

“文件指针”是内存的地址吗?

“文件指针”是文件中的一个地址吗?

C语言中的 “文件指针(FILE*)” 到底是个什么东西?
答:文件指针并非指针,而是一个数据结构。

C语言中使用文件指针作为I/O的句柄。

文件指针指向进程用户区中的一个被称为FILE结构的数据结构。FILE结构包括一个缓冲区和一个文件描述符。而文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在Windows系统上,文件描述符被称作文件句柄)。FILE *中除了包含了fd信息,还包含了IO缓冲,是C标准形式。

在这里插入图片描述
(by wuqijssn)

第02节:将数据写入文件

在这里插入图片描述在这里插入图片描述
TextFileOutput.cpp

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
  ofstream output;

  // Create a file
  output.open("scores.txt");

  // Write two lines
  output << "John" << " " << "T" << " " << "Smith"
    << " " << 90 << endl;
  output << "Eric" << " " << "K" << " " << "Jones"
    << " " << 85;

  output.close();

  cout << "Done" << endl;
  return 0;
}

在这里插入图片描述在这里插入图片描述
u07s02 - 如何将信息同时输出到文件和屏幕?
在软件的调试技术中,很重要的一个技术是将软件运行过程中的一些信息写入到“日志文件”中。但是同时还要将信息显示到屏幕上,以方便程序员实时查看这些信息。

对应于上面的要求,最简单的一种办法是这样的:

fstream output("debug.log", ios::out);
output << __FILE__ << ":" << __LINE__ << "\t" << "Variable x = " << x;
cout << __FILE__ << ":" << __LINE__ << "\t" << "Variable x = " << x;

不过,上面的代码看起来很愚蠢。

你知道有什么办法能解决问题并且避免上面这种蠢得让人流泪的代码吗?

(不限于使用程序解决问题。老崔提供两种办法:

  1. 只写日志文件debug.log,然后在linux控制台下使用 tail -f debug.log

  2. 只将信息输出屏幕上。如果程序编译链接后的名字叫做foo,则在linux下面使用 foo | tee debug.log)
    答:写一个自己的类,通过重载操作符同时写至两处。(by kevw22)

第03节:从文件读数据

在这里插入图片描述
TextFileInput.cpp

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  //ifstream input;
  ifstream input("scores.txt");
  
  // Open a file
  //input.open("scores.txt");

  // Read data
  char firstName[80];
  char mi;
  char lastName[80];
  int score;
  input >> firstName >> mi >> lastName >> score;
  cout << firstName << " " << mi << " " << lastName << " "
    << score << endl;

  input >> firstName >> mi >> lastName >> score;
  cout << firstName << " " << mi << " " << lastName << " "
    << score << endl;

  input.close();

  cout << "Done" << endl;
  return 0;
}

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
TestEndofFile.cpp

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  ifstream input;

  // Open a file
  input.open("scores1.txt");

  if (input.fail())
  {
    cout << "File does not exist" << endl;
    cout << "Exit program" << endl;
    return 0;
  }

  // Read data
  char firstName[60];
  char mi;
  char lastName[80];
  int score;

  while (!input.eof()) // Continue if not end of file
  {
    input >> firstName >> mi >> lastName >> score;
    cout << firstName << " " << mi << " " << lastName
      << " " << score << endl;
  }

  input.close();

  cout << "Done" << endl;
  return 0;
}

在这里插入图片描述

第04节:格式化输出

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述
WriteFormatData.cpp

#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;

int main()
{
  ofstream output;

  // Create a file
  output.open("formattedscores.txt");

  // Write two lines
  // Write two lines
  output << setw(6) << "John" << setw(2) << "T" << setw(6) << "Smith"
    << " " << setw(4) << 90 << endl;
  output << setw(6) << "Eric" << setw(2) << "K" << setw(6) << "Jones"
<< " " << setw(4) << 85;

  output.close();

  cout << "Done" << endl;
  return 0;
}

在这里插入图片描述
state.txt
New York#New Mexico#Texas#Indiana
ReadCity.cpp

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  ifstream input;

  // Open a file
  input.open("state.txt");

  if (input.fail())
  {
    cout << "File does not exist" << endl;
    cout << "Exit program" << endl;
    return 0;
  }

  // Read data
  char city[40];

  while (!input.eof()) // Continue if not end of file
  {
    input.getline(city, 40, '#');
    cout << city << endl;
  }

  input.close();

  cout << "Done" << endl;
  return 0;
}

在这里插入图片描述在这里插入图片描述
CopyFile.cpp

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  const int FILENAME_SIZE = 40;

  // Enter a source file
  cout << "Enter a source file name: ";
  char inputFilename[FILENAME_SIZE];
  cin >> inputFilename;

  // Enter a target file
  cout << "Enter a target file name: ";
  char outputFilename[FILENAME_SIZE];
  cin >> outputFilename;

  ifstream input;
  ofstream output;

  // Open a file
  input.open(inputFilename);
  output.open(outputFilename);

  if (input.fail())
  {
    cout << inputFilename << " does not exist" << endl;
    cout << "Exit program" << endl;
    return 0;
  }

  while (!input.eof()) // Continue if not end of file
  {
    output.put(input.get());
  }

  input.close();
  output.close();

  cout << "\nCopy Done" << endl;
  return 0;
}

u07s04 - 格式控制符到底是什么?
我们目前看到的流格式控制符既有 setw()这种函数形式,也有 left 这种非函数的形式。

流格式控制符本质上到底是什么?

你可以查资料,或者直接阅读 C++ 标准库中的源代码来了解。
答:left, right ,internal等都是IOS头文件中的一些标志,比如fmtflags setf (fmtflags fmtfl);中使用的fmtflags就是这些标志。

setw()函数等是在头文件ismanip中声明的一些函数。
关于setw(), 在cplusplus.com上是这么描述的:

Behaves as if member width were called with n as argument on the stream on which it is inserted/extracted as a manipulator (it can be inserted/extracted on input streams or output streams).

也就是说,其功效与ios_base的成员函数width() 类似。(by wuqijssn)

第05节:文件的打开模式与测试文件状态

在这里插入图片描述在这里插入图片描述

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  fstream inout;

  // Create a file
  inout.open("city.txt", ios::out);

  // Write cities
  inout << "Beijing" << " " << "Shanghai" << " " << "Guangzhou" << " ";

  inout.close();

  // Append to the file
  inout.open("city.txt", ios::out | ios::app);

  // Write cities
  inout << "Shenzhen" << " " << "Hongkong";

  inout.close();

  char city[20];

  // Open the file
  inout.open("city.txt", ios::in);
  while (!inout.eof()) // Continue if not end of file
  {
    inout >> city;
    cout << city << " ";
  }

  inout.close();

  return 0;
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述

#include <iostream>
#include <fstream>
using namespace std;

void showState(fstream &);

int main()
{
  fstream inout;

  // Create an output file
  inout.open("temp.txt", ios::out);
  inout << "Beijing";
  cout << "Normal operation (no errors)" << endl;
  showState(inout);
  inout.close();

  // Create an output file
  inout.open("temp.txt", ios::in);

  // Read a string
  char city[6];
  inout >> city;
  cout << "End of file (no errors)" << endl;
  showState(inout);

  inout.close();

  // Attempt to read after file closed
  inout >> city;
  cout << "Bad operation (errors)" << endl;
  showState(inout);

  return 0;
}

void showState(fstream & stream)
{
  cout << "Stream status: " << endl;
  cout << "  eof(): " << stream.eof() << endl;
  cout << "  fail(): " << stream.fail() << endl;
  cout << "  bad(): " << stream.bad() << endl;
  cout << "  good(): " << stream.good() << endl;
}

在这里插入图片描述

第07节:关于二进制读写的更多内容

在这里插入图片描述

#include <iostream>
#include <fstream>
using namespace std;

int main()
{
  const int SIZE = 5;  // Array size

  fstream binaryio; // Create stream object

  // Write array to the file
  binaryio.open("array.dat", ios::out | ios::binary);
  double array[SIZE] = {3.4, 1.3, 2.5, 5.66, 6.9};
  binaryio.write(reinterpret_cast<char *>(array), sizeof(array));
  binaryio.close();

  // Read array from the file
  binaryio.open("array.dat", ios::in | ios::binary);
  double result[SIZE];
  binaryio.read(reinterpret_cast<char *>(result), sizeof(result));
  binaryio.close();

  // Display array
  for (int i = 0; i < SIZE; i++)
    cout << result[i] << " ";

  return 0;
}

在这里插入图片描述在这里插入图片描述在这里插入图片描述
u07s07 - 关于对象中的静态数据成员如何写入到文件?
如果一个类C中有一个静态数据成员 static int x;

c1 和 c2 均为 C 的对象

那么执行如下代码后

fstream fs("out.dat", ios::out|ios::binary);
fs.write(reinterpret_cast<char*>(&c1), sizeof(C));
fs.write(reinterpret_cast<char*>(&c2), sizeof(C));

out.dat 文件中是否有x的值?

如果有的话,其中保存了几个 x 值的副本?
答:貌似是两个,每个一份。

实验:创建myClass,含一个静态int。往oneObject.dat写入一个对象,往twoObjects.dat写入两个对象。
在这里插入图片描述
(by kevw22)

第08节:随机访问文件

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

#include <iostream>
#include <fstream>
#include "Student.h"
using namespace std;

void displayStudent(Student student)
{
  cout << student.getFirstName() << " ";
  cout << student.getMi() << " ";
  cout << student.getLastName() << " ";
  cout << student.getScore() << endl;
}

int main()
{
  fstream binaryio; // Create stream object
  binaryio.open("object1.dat", ios::out | ios::binary);

  Student student1("Student1", 'T', "Smith", 90);
  Student student2("Student2", 'T', "Smith", 90);
  Student student3("Student3", 'T', "Smith", 90);
  Student student4("Student4", 'T', "Smith", 90);
  Student student5("Student5", 'T', "Smith", 90);
  Student student6("Student6", 'T', "Smith", 90);
  Student student7("Student7", 'T', "Smith", 90);
  Student student8("Student8", 'T', "Smith", 90);
  Student student9("Student9", 'T', "Smith", 90);
  Student student10("Student10", 'T', "Smith", 90);

  binaryio.write(reinterpret_cast<char *>
    (&student1), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student2), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student3), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student4), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student5), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student6), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student7), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student8), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student9), sizeof(Student));
  binaryio.write(reinterpret_cast<char *>
    (&student10), sizeof(Student));

  binaryio.close();

  // Read student back from the file
  binaryio.open("object1.dat", ios::in | ios::binary);

  Student studentNew;

  binaryio.seekg(2 * sizeof(Student));

  cout << "Current position is " << binaryio.tellg() << endl;

  binaryio.read(reinterpret_cast<char *>
    (& studentNew), sizeof(Student));

  displayStudent(studentNew);

  cout << "Current position is " << binaryio.tellg() << endl;

  binaryio.close();

  return 0;
}

在这里插入图片描述

在这里插入图片描述u07s08 - fstream中是否有两个文件指针,一个读指针一个写指针?
请你写一段代码:

构造一个fstream的对象并打开文件读写;

使用tellp和tellg获取指针位置

读或者写文件

再使用tellp和tellg获取指针位置

然后尝试回答如下问题:

读文件对tellp和tellg的返回结果有影响吗?

写文件对tellp和tellg的返回结果有影响吗?

tellp和tellg的调用次序对结果有影响吗?
答:

#include <iostream>
#include <fstream>
using namespace std;
int main()
{
    fstream inout;
    inout.open("123.dat", ios::out | ios::binary);
    cout << "tellp = " << inout.tellp() << " tellg = " << inout.tellg() << endl;
    cout << "tellg = " << inout.tellg() << " tellp = " << inout.tellp() << endl; // tellp 和 tellg 交换顺序
    inout.write("12345", 5);
    cout << "tellp = " << inout.tellp() << " tellg = " << inout.tellg() << endl;
    cout << "tellg = " << inout.tellg() << " tellp = " << inout.tellp() << endl; // tellp 和 tellg 交换顺序
    return 0;
}

运行结果
在这里插入图片描述
分析:

1.读文件对tellp和tellg的返回结果有影响吗?

有影响,影响和写文件一样(类比得到)

2.写文件对tellp和tellg的返回结果有影响吗?

tellp和tellg转到了写之后的地方

3.tellp和tellg的调用次序对结果有影响吗?

无(by Neymar233)

猜你喜欢

转载自blog.csdn.net/dldldl1994/article/details/86898709