66、文件输入输出操作全解析

文件输入输出操作全解析

1. 文件流的关闭与重新打开

文件流对象销毁时,文件会自动关闭,但也能手动关闭输入流,方式与关闭输出流相同,示例代码如下:

inFile().close(); // 关闭输入流

调用 close() 后,文件将无法再被读取。若显式关闭文件流,可通过调用 open() 以不同的打开模式重新打开, open() 接受文件名和打开模式两个参数,第二个参数有默认值,示例如下:

outFile.close();
outFile.open(filename); // 重新打开文件,覆盖原内容
outFile.open(filename, std::ios::out|std::ios::app); // 重新打开文件,追加数据
2. 设置流的打开模式

ifstream ofstream 对象的打开模式决定了对文件的操作方式。打开模式由 ios_base 中定义并通过 ios 类继承到流类的 openmode 类型的位掩码值组合而成。 ifstream ofstream 对象的构造函数的第二个参数为 openmode 类型,有默认值。以下是常见的打开模式值及其含义:
| 值 | 含义 |
| ---- | ---- |
| ios::app | 每次写入前移动到文件末尾(追加操作),确保只能在文件现有内容后添加数据,不能覆盖 |
| ios::ate | 打开文件后移动到文件末尾,之后可将当前位置移动到文件其他位置 |
| ios::binary | 设置为二进制模式,二进制模式下,字符在文件传输时保持不变,未设置则为文本模式 |
| ios::in | 打开文件用于读取 |
| ios::out | 打开文件用于写入 |
| ios::trunc | 将现有文件截断为零长度 |

打开模式规范由一个或多个 openmode 值组成,通过按位或运算组合这些值。例如,以二进制模式打开文件并只能在末尾追加数据,可指定模式为 ios::out|ios::app|ios::binary 。若要同时以读写模式打开文件,需使用 fstream 对象并指定 ios::in ios::out ifstream 对象的默认打开模式是 ios::in ofstream 对象的默认打开模式是 ios::out 。可在文件流构造函数的第二个参数中指定打开模式,示例如下:

std::string filename {"D:\\Example_Data\\primes.txt"};
std::ofstream outFile {filename, std::ios::out|std::ios::app};
// 等同于
std::ofstream outFile {filename, std::ios::app};
3. 生成并处理素数文件的示例程序

以下是一个示例程序,用于生成并显示所需数量的素数,若 primes.txt 文件已存在,会读取并显示文件中的素数,同时将新找到的素数添加到文件中:

// Ex17_03.cpp
// Reading and writing the primes file
#include <fstream>
#include <iostream>
#include <iomanip>
#include <cmath>
#include <string>
using std::ios;
using std::string;
using ulong = unsigned long long;

ulong nextprime(ulong aprime, const string filename);  // Find the prime following aprime

int main()
{
  string filename {"D:\\Example_Data\\more_primes.txt"};
  size_t nprimes {};                             // Number of primes required
  size_t count {};                               // Count of primes found
  ulong lastprime {};                            // Last prime found
  size_t perline {5};                            // Number output per line

  // Get number of primes required
  std::cout << "How many primes would you like (at least 4)?: ";
  std::cin >> nprimes;
  if (nprimes < 4) nprimes = 4;

  std::ifstream inFile;                          // Create input file stream
  inFile.open(filename);                         // Open the filename file for input

  if (!inFile.fail())                            // If there is a file...
  {                                              // ...read primes from it
    while (true)
    {
      inFile >> lastprime;
      if (inFile.eof()) break;

      std::cout << std::setw(10) << lastprime << (++count % perline == 0 ? "\n" : "");
      if (count == nprimes) break;
    }
    inFile.close();                              // Reading is finished
    if (count == nprimes)                        // Check if we found them all
      {
        inFile.close();                          // We are done with the file
        std::cout << std::endl << count << "primes found in file." << std::endl;
        return 0;
      }
  }

  // If we get to here, we need to find more primes
  inFile.clear();                                // Clear EOF flag
  inFile.close();                                // Reading is finished
  try
  {
    size_t oldCount {count};                     // The number that were in the file
    std::ofstream outFile;                       // Create an output stream object

    if (oldCount == 0)
    { // The file is empty
      outFile.open(filename);                    // Open file to create it
      if (!outFile.is_open())
        throw ios::failure {string {"Error opening output  file "} + filename + " in main()"};

      outFile << "2\n3\n5\n";                    // Write 1st three primes to file
      outFile.close();
      std::cout << std::setw(10) << 2 << std::setw(10) << 3 << std::setw(10) << 5;
      lastprime = 5;
      count = 3;
    }

    while (count < nprimes)
    {
      lastprime = nextprime(lastprime, filename);
      outFile.open(filename, ios::out | ios::app); // Open file to append data
      if (!outFile.is_open())
        throw ios::failure {string {"Error opening output file "} + filename + " in main()"};
      outFile << lastprime << '\n';
      outFile.close();
      std::cout << std::setw(10) << lastprime << (++count % perline == 0 ? "\n" : "");
    }
    std::cout << std::endl << nprimes << " primes found. "
              << nprimes-oldCount << " added to file." << std::endl;
  }
  catch (std::exception& ex)
  {
    std::cout << typeid(ex).name() << ": " << ex.what() << std::endl;
    return 1;
  }
}

ulong nextprime(ulong last, const string filename)
{
  bool isprime {false};                          // true when we have a prime
  ulong aprime {};                               // Stores a prime from the file
  std::ifstream inFile;                          // Local file input stream object

  // Find the next prime
  ulong limit {};
  while (true)
  {
    last += 2ULL;                                // Next value for checking
    limit = static_cast<ulong>(std::sqrt(last));

    // Try dividing the candidate by all the primes up to limit
    inFile.open(filename);                       // Open the primes file
    if (!inFile.is_open())
      throw ios::failure {string {"Error opening input file "} +filename + " in nextprime()"};

    do
    {
      inFile >> aprime;
    } while (aprime <= limit && !inFile.eof() && (isprime = last % aprime > 0));
    inFile.close();
    if (isprime)                                 // We got one...
      return last;                               // ...so return it
  }
}

该程序的执行流程如下:
1. 获取用户所需的素数数量。
2. 尝试打开 more_primes.txt 文件进行读取,若文件存在且包含足够的素数,读取并显示这些素数,然后结束程序。
3. 若文件不存在或文件中的素数数量不足,清除文件流的 EOF 标志并关闭文件。
4. 若文件为空,创建文件并写入前三个素数(2、3、5)。
5. 在 while 循环中,调用 nextprime() 函数找到下一个素数,以追加模式打开文件并将新素数写入文件,同时显示该素数。

4. 管理当前流位置

可通过 istream ostream 类的成员函数访问和更改当前流位置,这些函数不适用于标准流,适用于所有文件流类对象。 istream 中的 tellg() 函数用于获取输入流对象的当前位置, ostream 中的 tellp() 函数用于获取输出流对象的当前位置,二者返回 pos_type 类型的值,表示流中的绝对位置。示例如下:

std::ifstream::pos_type here {inFile.tellg()}; // 记录当前文件位置
// 可使用 auto 简化
auto here = inFile.tellg();

通过 seekg() 函数可将输入流对象的位置设置为指定位置, seekp() 函数用于输出流对象。例如,将 inFile 的流位置重置为 here

inFile.seekg(here);

也可相对于流中的三个特定位置(开头、末尾、当前位置)以偏移量指定新位置,偏移量可为正或负。例如:

graph LR
    A[输入文件流] --> B[ios::beg]
    A --> C[ios::cur]
    A --> D[ios::end]
    B --> E[正向偏移]
    C --> F[正向偏移]
    C --> G[负向偏移]
    D --> H[负向偏移]
    E --> I[inFile.seekg(2, std::ios::beg)]
    F --> J[inFile.seekg(-2, std::ios::cur)]
    H --> K[inFile.seekg(-4, std::ios::end)]

nextprime() 函数中,可通过将文件位置重置为开头来避免每次循环都关闭和重新打开文件,优化后的代码如下:

ulong nextprime(ulong last, const string filename)
{
  bool isprime {false};                          // true when we have a prime
  ulong aprime {};                               // Stores a prime from the file
  std::ifstream inFile(filename);                // Local file input stream object
  if (!inFile.is_open())
    throw ios::failure {string {"Error opening input file "} + filename + " in nextprime()"};

  // Find the next prime
  ulong limit {};
  while (true)
  {
    last += 2ULL;                                // Next value for checking
    limit = static_cast<ulong>(std::sqrt(last));

    // Try dividing the candidate by all the primes up to limit
    do
    {
      inFile >> aprime;
    } while (aprime <= limit && !inFile.eof() && (isprime = last % aprime > 0));

    inFile.seekg(0);
    if (isprime)                                 // We got one...
    {
      inFile.close();                            // ...close the file...
      return last;                               // ...and return the prime
    }
  }
}

通过优化,每次循环只需将文件位置重置为开头,找到素数后再关闭文件,提高了程序效率。

文件输入输出操作全解析

5. 流位置操作的详细解释

流位置操作对于文件输入输出至关重要,下面详细解释获取和更改流位置的操作。

  • 获取当前流位置
  • tellg() 用于输入流, tellp() 用于输出流,返回 pos_type 类型的值,代表流中的绝对位置。例如:
std::ifstream inFile("example.txt");
std::ifstream::pos_type currentPos = inFile.tellg();
// 或者使用 auto 简化
auto currentPosAuto = inFile.tellg();

这里 currentPos currentPosAuto 都记录了 inFile 的当前文件位置。

  • 更改当前流位置
  • seekg() 用于输入流, seekp() 用于输出流。可以使用绝对位置或相对位置来设置。
    • 绝对位置
std::ifstream inFile("example.txt");
std::ifstream::pos_type targetPos = 10; // 假设要移动到位置 10
inFile.seekg(targetPos);
- **相对位置**:可以相对于流的开头(`ios::beg`)、当前位置(`ios::cur`)、末尾(`ios::end`)进行偏移。偏移量可以是正或负。
std::ifstream inFile("example.txt");
// 从开头偏移 5 个位置
inFile.seekg(5, std::ios::beg);
// 从当前位置向后偏移 3 个位置
inFile.seekg(3, std::ios::cur);
// 从末尾向前偏移 2 个位置
inFile.seekg(-2, std::ios::end);

以下是一个表格总结流位置操作函数:
| 函数 | 适用流类型 | 作用 |
| ---- | ---- | ---- |
| tellg() | 输入流 | 获取当前输入流位置 |
| tellp() | 输出流 | 获取当前输出流位置 |
| seekg() | 输入流 | 设置输入流的位置 |
| seekp() | 输出流 | 设置输出流的位置 |

6. 流位置操作在素数程序中的优化效果

在之前的素数程序中, nextprime() 函数每次循环都打开和关闭文件,效率较低。通过使用流位置操作进行优化后,效果显著。

优化前的 nextprime() 函数:

ulong nextprime(ulong last, const string filename)
{
  bool isprime {false};
  ulong aprime {};
  std::ifstream inFile;

  ulong limit {};
  while (true)
  {
    last += 2ULL;
    limit = static_cast<ulong>(std::sqrt(last));

    inFile.open(filename);
    if (!inFile.is_open())
      throw ios::failure {string {"Error opening input file "} +filename + " in nextprime()"};

    do
    {
      inFile >> aprime;
    } while (aprime <= limit && !inFile.eof() && (isprime = last % aprime > 0));
    inFile.close();
    if (isprime)
      return last;
  }
}

优化后的 nextprime() 函数:

ulong nextprime(ulong last, const string filename)
{
  bool isprime {false};
  ulong aprime {};
  std::ifstream inFile(filename);
  if (!inFile.is_open())
    throw ios::failure {string {"Error opening input file "} + filename + " in nextprime()"};

  ulong limit {};
  while (true)
  {
    last += 2ULL;
    limit = static_cast<ulong>(std::sqrt(last));

    do
    {
      inFile >> aprime;
    } while (aprime <= limit && !inFile.eof() && (isprime = last % aprime > 0));

    inFile.seekg(0);
    if (isprime)
    {
      inFile.close();
      return last;
    }
  }
}

优化后的流程如下:
1. 函数开始时打开文件。
2. 在循环中,每次检查新的素数候选值。
3. 循环结束后,使用 seekg(0) 将文件位置重置为开头,避免了频繁打开和关闭文件。
4. 找到素数后关闭文件并返回结果。

通过这种优化,减少了文件打开和关闭的开销,提高了程序的执行效率。

7. 异常处理在文件操作中的重要性

在文件输入输出操作中,异常处理是必不可少的。如在素数程序中,使用 try-catch 块来捕获可能出现的异常。

try
{
  size_t oldCount {count};
  std::ofstream outFile;

  if (oldCount == 0)
  {
    outFile.open(filename);
    if (!outFile.is_open())
      throw ios::failure {string {"Error opening output  file "} + filename + " in main()"};

    outFile << "2\n3\n5\n";
    outFile.close();
    std::cout << std::setw(10) << 2 << std::setw(10) << 3 << std::setw(10) << 5;
    lastprime = 5;
    count = 3;
  }

  while (count < nprimes)
  {
    lastprime = nextprime(lastprime, filename);
    outFile.open(filename, ios::out | ios::app);
    if (!outFile.is_open())
      throw ios::failure {string {"Error opening output file "} + filename + " in main()"};
    outFile << lastprime << '\n';
    outFile.close();
    std::cout << std::setw(10) << lastprime << (++count % perline == 0 ? "\n" : "");
  }
  std::cout << std::endl << nprimes << " primes found. "
            << nprimes-oldCount << " added to file." << std::endl;
}
catch (std::exception& ex)
{
  std::cout << typeid(ex).name() << ": " << ex.what() << std::endl;
  return 1;
}

在上述代码中:
- 当打开文件失败时,会抛出 ios::failure 异常。
- catch 块捕获异常并输出异常信息,避免程序崩溃。

异常处理的步骤如下:
1. 在 try 块中执行可能抛出异常的操作,如打开文件、写入数据等。
2. 当出现异常时,抛出相应的异常对象。
3. catch 块捕获异常并进行处理,如输出错误信息、进行恢复操作等。

通过异常处理,可以增强程序的健壮性,提高程序在面对错误时的处理能力。

8. 总结

文件输入输出操作涉及多个方面,包括文件流的关闭与重新打开、流打开模式的设置、流位置的管理以及异常处理等。

  • 文件流的关闭和重新打开可以灵活控制文件的访问。
  • 合理设置流打开模式可以满足不同的文件操作需求,如读写、追加等。
  • 流位置操作可以提高文件操作的效率,避免不必要的文件打开和关闭。
  • 异常处理可以增强程序的健壮性,提高程序的稳定性。

在实际编程中,需要综合运用这些知识,根据具体需求进行合理的文件输入输出操作,以实现高效、稳定的程序。

下面是一个简单的 mermaid 流程图,总结整个素数程序的执行流程:

graph TD
    A[获取用户所需素数数量] --> B[尝试打开文件读取素数]
    B --> C{文件存在且素数足够?}
    C -- 是 --> D[显示素数并结束程序]
    C -- 否 --> E[清除 EOF 标志并关闭文件]
    E --> F{文件为空?}
    F -- 是 --> G[创建文件并写入前三个素数]
    F -- 否 --> H[继续寻找新素数]
    G --> H
    H --> I[调用 nextprime() 找下一个素数]
    I --> J[以追加模式打开文件写入新素数]
    J --> K[显示新素数]
    K --> L{素数数量达到要求?}
    L -- 否 --> I
    L -- 是 --> M[结束程序]

通过这个流程图,可以清晰地看到素数程序的整体执行逻辑,有助于理解和优化程序。

【最优潮流】直流最优潮流(OPF)课设(Matlab代码实现)内容概要:本文档主要围绕“直流最优潮流(OPF)课设”的Matlab代码实现展开,属于电力系统优化领域的教学与科研实践内容。文档介绍了通过Matlab进行电力系统最优潮流计算的基本原理与编程实现方法,重点聚焦于直流最优潮流模型的构建与求解过程,适用于课程设计或科研入门实践。文中提及使用YALMIP等优化工具包进行建模,并提供了相关资源下载链接,便于读者复现与学习。此外,文档还列举了大量与电力系统、智能优化算法、机器学习、路径规划等相关的Matlab仿真案例,体现出其服务于科研仿真辅导的综合性平台性质。; 适合人群:电气工程、自动化、电力系统及相关专业的本科生、研究生,以及从事电力系统优化、智能算法应用研究的科研人员。; 使用场景及目标:①掌握直流最优潮流的基本原理与Matlab实现方法;②完成课程设计或科研项目中的电力系统优化任务;③借助提供的丰富案例资源,拓展在智能优化、状态估计、微电网调度等方向的研究思路与技术手段。; 阅读建议:建议读者结合文档中提供的网盘资源,下载完整代码与工具包,边学习理论边动手实践。重点关注YALMIP工具的使用方法,并通过复现文中提到的多个案例,加深对电力系统优化问题建模与求解的理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值