【问题标题】:File handling with Text使用文本处理文件
【发布时间】:2019-10-10 11:05:51
【问题描述】:

我正在读取一个包含文本和整数的文件。 需要提取唯一的整数跳过文本。

我已经实现了读取整数的代码,但是如何跳过介于两者之间的文本并继续读取整数。

输入:

01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
some text
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
01000000 01000000 01000000 01000000
#include<bits/stdc++.h>
using namespace std;
int main(void){
 unsigned int number1,number2,number3,number4; 

          FILE* in_file = fopen("example.txt", "r"); 
           FILE* in_file1 = fopen("wrte.txt", "w"); 
           if (! in_file ) 
             {  
                printf("oops, file can't be read\n"); 
                exit(-1); 
             } 

          // attempt to read the next line and store 
          // the value in the "number" variable 

          while (fscanf(in_file,"%08x", &number1) == 1){ 
                fprintf(in_file1,"%08x\n", number1); 
             }
    fclose(in_file1);
    fclose(in_file);
return 0;
}

Expected output : Each 01000000 in a single line without text in it

【问题讨论】:

标签: c++ string file numbers scanf


【解决方案1】:

我几乎从不依赖 fscanf 之类的东西。

我会阅读文本行并智能地解析它们。如果您知道该行是用空格分隔的,则可以在空格处拆分,然后唯一地查看每个块。 if (isdigit(chunk 中的第一个字符)) then int value = atoi(chunk).

在调用 atoi 之前,您甚至可以更加小心并确保整个块代表一个合法数字。

【讨论】:

    【解决方案2】:

    我可以想到几个简单的方法来做到这一点。

    您可以将所有内容读取为string,然后丢弃任何无法转换为整数的内容。

    std::string token;
    while (filein >> token) // read string, exit on failure
    {
        try 
        {
            int value = std::stoul(token, 0, 16); // try to turn string into integer
            use value        
        }
        catch (const std::invalid_argument &) // couldn't convert. do nothing
        {
        }
    }
    

    Documentation for std::stoul.

    如果非数字数据很频繁(因为它不是例外),您可能不想使用想要使用异常。在这种情况下,请考虑使用 strtoul 并自己执行错误处理。

    Documentation for strtoul.

    您还可以使用&gt;&gt; 将整数读取为整数并检查是否成功。当读取失败时,clear 失败位并读取为string。如果您无法读取string,则该文件已损坏或完全消耗。停止阅读。如果你能读懂string,就把它扔掉,然后继续读整数

    while (true)
    {
        int value;
        if (filein >> std::hex >> value) // read a number
        {
            use value
        }
        else // failed to read number
        {
            filein.clear(); // clear fail bit
            std::string junk;
            if (!(filein >> junk)) // read a string
            {
                break; // no more readable data. exit loop
            }
             // do nothing with junk.
        }
    }
    

    您可以使用ignore 对此进行改进,并在尝试读取string 之前检查文件结尾并退出

    Documentation for std::stoul.

    【讨论】:

      【解决方案3】:

      虽然您通常会使用 C++ iostream 库进行文件 I/O,但没有任何内容表明您不能使用诸如 fscanf 之类的 C 函数 fscanf -- 只要您正确使用它们(通常它们会比iostream 方法更快)

      在您的情况下,您有很多数字,中间有一些文本,您正试图在循环中使用fscanf 阅读。很好,很简单,但是……你必须正确处理 匹配失败 情况当您尝试使用"%08x" 转换说明符 读取's' 时会发生。

      当发生匹配失败时,从流中提取的字符会在失败点停止,以导致失败的字符开头的所有内容(以及后面的内容)未读 在输入缓冲区中。除非您从输入流中正确提取导致 匹配失败 的字符,否则您可能会遇到无限循环,因为导致失败的字符仍未被读取,只是在等待下一次尝试读取时再次咬您。

      那么,如何处理匹配失败cctype 标头提供了 isdigit 宏,它允许您简单地测试输入流中的下一个字符是否是数字。您首先使用fgetc(或getc - 相同但通常作为宏实现)读取字符,然后使用isdigit 进行测试,例如

                  int c = fgetc(in_file);             /* read next char */
                  while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                      c = fgetc(in_file);             /* get next char */
      

      在您阅读下一个字符的上方,然后输入一个循环来验证您尚未到达EOF,然后检查c 是否不是数字。如果满足这些条件,则再次检查下一个字符,直到达到EOF 或者在输入流中找到下一个数字。但是现在你有一个问题,你已经从流中读取了数字,fscanf 怎么能将它作为下一个整数的一部分读取?

      简单——放回输入流中:

                  if (c != EOF)                       /* if not EOF, then digit */
                      ungetc (c, in_file);            /* put back for next read */
      

      现在您可以通过简单的循环从in_file 中读取所有 64 个整数值,例如

          while (1) { /* loop continually until EOF */
              int rtn = fscanf (in_file,"%08x", &number1);    /* validate return */
              if (rtn == EOF)         /* if EOF, break loop */
                  break;
              else if (rtn == 0) {    /* handle matching failure */
                  int c = fgetc(in_file);             /* read next char */
                  while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                      c = fgetc(in_file);             /* get next char */
                  if (c != EOF)                       /* if not EOF, then digit */
                      ungetc (c, in_file);            /* put back for next read */
              }
              else    /* good read, output number */
                  fprintf (out_file, "%08x\n", number1); 
          }
      

      注意:您的输出文件已从 in_file1 重命名为 out_file -- 始终使用有意义的变量名)

      现在进行一些清理。当您打开 in_file 时,您验证文件是否已打开以供阅读。很好,但是对于您exit (-1); 的错误情况。不要向外壳返回负值。您有两个常量来指示成功/失败名称EXIT_SUCCESS (0) 和EXIT_FAILURE(值1,不是-1)。

      虽然您确实检查了in_file 是否已打开以供读取,但您完全没有检查您的输出文件是否已打开以供写入?始终验证所有输入/输出流和 I/O 函数的返回。否则尝试写入处于错误状态的流会调用未定义行为。

      总而言之,你可以这样做:

      #include <cstdio>
      #include <cstdlib>
      #include <cctype>
      
      using namespace std;
      
      int main (void) {
      
          unsigned int number1; 
      
          FILE* in_file = fopen ("example.txt", "r"); 
          FILE* out_file = fopen ("wrte.txt", "w"); 
      
          if (!in_file) {     /* validate file open for reading */
              printf ("oops, file can't be read\n"); 
              exit (1);       /* don't return negative values to the shell */
          }
          if (!out_file) {    /* validate file open for writing */
              printf ("oops, file can't be read\n"); 
              exit (1);       /* don't return negative values to the shell */
          }
      
          while (1) { /* loop continually until EOF */
              int rtn = fscanf (in_file,"%08x", &number1);    /* validate return */
              if (rtn == EOF)         /* if EOF, break loop */
                  break;
              else if (rtn == 0) {    /* handle matching failure */
                  int c = fgetc(in_file);             /* read next char */
                  while (c != EOF && !isdigit(c))     /* check EOF and isdigit */
                      c = fgetc(in_file);             /* get next char */
                  if (c != EOF)                       /* if not EOF, then digit */
                      ungetc (c, in_file);            /* put back for next read */
              }
              else    /* good read, output number */
                  fprintf (out_file, "%08x\n", number1); 
          }
          fclose (in_file);
          fclose (out_file);
      }
      

      示例输出文件

      $ cat wrte.txt
      01000000
      01000000
      01000000
      01000000
      ...
      01000000
      

      所有 64 个值均已写入,您可以使用 wc -l 确认,例如

      $ wc -l < wrte.txt
      64
      

      查看一下,如果您还有其他问题,请告诉我。如果您使用的是 iostream 库,同样的逻辑也适用,函数名称略有不同(有些相同),而是作为成员函数实现。

      【讨论】:

        【解决方案4】:

        当然,这是一个非常简单的任务,您只需稍微修改循环并使用 feof 作为循环条件:

        while(feof(in_file) == 0) {
            if(fscanf(in_file, " %8x ", &number1) > 0) {
                fprintf(in_file1,"%08x\n", number1);
            } else {
                fscanf(in_file, " %*s ");
            }
        }
        

        Live Example

        我还想建议你放弃FILE*,开始在 中使用fstreams,但这只是一个方便的建议。

        【讨论】:

          【解决方案5】:

          这是一个强调简单的可读解决方案:

          // -*- compile-command: "g++ data.cpp; ./a.out "; -*-
          #include <fstream>
          #include <iomanip>
          #include <iterator>
          #include <string>
          
          int main()
          {
            std::ifstream fin("data.in");
            std::ofstream fout("data.out");
          
            auto fin_iter = std::istream_iterator<std::string>(fin);
            const auto fin_iter_end = std::istream_iterator<std::string>();
          
            while (fin_iter != fin_iter_end)
            {
              try
              {
                fout << std::setfill('0') << std::setw(8) << std::stoul(*fin_iter) << " ";
              }
              catch (...)
              {
              }
              ++fin_iter;
            };
          
            fin.close();
            fout.close();
          
            return 0;
          }
          

          这里是“想法”:

          要回答您的问题,您可以“跳过整数之间的文本”,因为在这种情况下 stoul 会引发异常。如果我们捕捉到一个异常,我们什么也不做,否则我们将转换后的整数写入输出文件。

          data.in

          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          some text
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          01000000 01000000 01000000 01000000
          

          data.out

          01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000 01000000
          

          【讨论】:

            猜你喜欢
            • 2022-10-18
            • 1970-01-01
            • 1970-01-01
            • 2016-09-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-06-23
            • 1970-01-01
            相关资源
            最近更新 更多