Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/72.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C语言读取列式文本文件_C_Printf_Scanf - Fatal编程技术网

C语言读取列式文本文件

C语言读取列式文本文件,c,printf,scanf,C,Printf,Scanf,首先,如果这件事太琐碎,请原谅,我不是C开发人员,通常我用Fortran编程 我需要阅读一些列式文本文件。我遇到的问题是,有些列可能有空格(非填充值)或未完全归档字段 让我用一个简单的例子来说明这个问题。假设我有一个生成器程序,如: #include <stdio.h> #include <stdlib.h> int main(){ printf("xxxx%4d%4.2f\n",99,3.14); } 如果我将其放入文本文件并尝试使用(例如)sscanf和

首先,如果这件事太琐碎,请原谅,我不是C开发人员,通常我用Fortran编程

我需要阅读一些列式文本文件。我遇到的问题是,有些列可能有空格(非填充值)或未完全归档字段

让我用一个简单的例子来说明这个问题。假设我有一个生成器程序,如:

#include <stdio.h>
#include <stdlib.h>

int main(){

   printf("xxxx%4d%4.2f\n",99,3.14);

}
如果我将其放入文本文件并尝试使用(例如)sscanf和代码进行读取:

#include <stdio.h>
#include <stdlib.h>

int main() {

   char *fmt = "%*4c%4d%4f";
   char *line = "xxxx  993.14";
   int  ival;
   float fval;

   sscanf(line,fmt,&ival,&fval);

   printf(">>>>%d|%f\n",ival,fval);
}
这里有什么问题?sscanf似乎认为所有空间都是毫无意义的,应该丢弃。因此“%4c”按其本意执行,它计数4个字符,但不丢弃任何空格,并丢弃因“”而产生的所有内容。接下来,%4d开始跳过所有空格,并在找到转换的第一个有效字符时开始计算字段的4个字符。所以这个值,意味着99变成了993,3.14变成了0.14

在Fortran中,读取代码为:

program t3

   implicit none

   integer :: ival
   real    :: fval
   character(len=30) :: fmt="(4x,i4,f4.0)"
   character(len=30) :: line="xxxx  993.14"

   read(line,fmt) ival, fval
   write(*,"('>>>>',i4,'|',f4.2)") ival,fval

end program t3
$ ./t3
>>>>  99|3.14
结果是:

program t3

   implicit none

   integer :: ival
   real    :: fval
   character(len=30) :: fmt="(4x,i4,f4.0)"
   character(len=30) :: line="xxxx  993.14"

   read(line,fmt) ival, fval
   write(*,"('>>>>',i4,'|',f4.2)") ival,fval

end program t3
$ ./t3
>>>>  99|3.14
也就是说,格式规范规定了字段宽度,并且在转换过程中不会丢弃任何内容,除非“nX”规范指示这样做

帮助助手的最后几句话:

  • 要读取的格式是国际标准,没有 改变它的方法
  • 现有文件的数量太大,无法考虑干预或删除 格式更改
  • 它不是CSV或类似格式
  • 代码必须是C语言,才能集成到自由软件包中

    抱歉,时间太长,请尽可能完整地说明问题

    问题是:有没有办法告诉sSCANF不要跳过空白区?如果没有,是否有一种简单的方法在C中实现,或者有必要为每种记录类型编写一个专门的解析器

    先谢谢你


  • 如果每个元素的宽度固定,您实际上不需要
    scanf()
    ,请尝试以下操作

    char copy[5];
    const char *line = "xxxx  993.14";
    int ival;
    float fval;
    
    copy[0] = line[4];
    copy[1] = line[5];
    copy[2] = line[6];
    copy[3] = line[7];
    copy[4] = '\0'; // nul terminate for `atoi' to work
    
    ival = atoi(copy);
    fval = atof(&line[8]);
    
    fprintf(stdout, "%d -- %f\n", ival, fval);
    
    如果需要(可能应该),可以使用
    strtol()
    而不是
    atoi()
    strtof()
    而不是
    atof()
    来检查格式错误的数据

    这两个函数都使用一个参数来存储未转换/无效字符,您可以检查传递的指针以验证转换是否存在问题

    或者,如果确实需要
    scanf()
    执行相同的操作,请将整数+空格捕获到
    char
    数组中,然后稍后将其转换为
    int
    ,如下所示

    char integer[5];
    const char *line = "xxxx  993.14";
    int ival;
    float fval;
    
    if (sscanf(line, "%*4c%4[0-9 ]%f", integer, &fval) != 2)
        return -1;
    ival = atoi(integer);
    
    fprintf(stdout, "%d -- %f\n", ival, fval);
    
    格式
    “%*4c%4[0-9]%f”

  • 跳过前四个字符,包括空格
  • 如果后面的四个字符仅由数字或空格组成,请扫描它们
  • 扫描输入字符串的其余部分,搜索匹配的
    float

  • 首先,我不知道。可能有一些方法可以让sscanf识别整数计数中的空格。但我只是不认为scanf是为这种格式而设计的。这个工具试图变得聪明而有用,它在咬你的屁股

    但是如果它是列数据,并且您知道各个字段的位置,那么有一个非常简单的解决方法。只需提取所需的字段

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argc, char** argv)
    {
      char line[] = "xxxx  893.14";
      char tmp[100];
      int thatDamnNumber;
      float myfloatykins;
    
      //Get that field
      memcpy(tmp, line+4, 4);
      sscanf(tmp, "%d", &thatDamnNumber);
    
      //Kill that field so it doesn't goober-up the float
      memset(line+4, ' ', 4);
      sscanf(line, "%*4c%f", &myfloatykins);
    
      printf("%d %f\n", thatDamnNumber, myfloatykins);
    
      return 0;
    
    }
    
    #包括
    #包括
    #包括
    int main(int argc,字符**argv)
    {
    字符行[]=“xxxx 893.14”;
    char-tmp[100];
    int thatDamnNumber;
    浮动myfloatykins;
    //去那块地
    memcpy(tmp,线路+4,4);
    sscanf(tmp、%d、&thatDamnNumber);
    //杀了那块地,这样它就不会把浮子弄脏
    memset(行+4',,4);
    sscanf(行“%*4c%f”和myfloatykins);
    printf(“%d%f\n”,thatDamnNumber,myfloatykins);
    返回0;
    }
    

    如果有很多这样的函数,您可以生成一些通用函数:integerExtract(int-positionStart,int-sizeInCharacters),flootextract(),等等。

    当使用
    sscanf
    读取固定长度字段时,最好将值解析为字符串(您可以使用多种方法),然后对每个字段执行独立转换。这允许您在每个字段的基础上处理转换/错误检测。例如,可以使用以下格式字符串:

    char *fmt = "%*4s%2[^0-9]%s";
    
    它将读取/丢弃4个前导字符,然后读取2个字符作为整数,后跟
    的剩余部分(或直到下一个空格),作为包含浮点值的字符串

    要将
    作为固定长度字段进行存储和解析,可以使用临时字符数组保存每个字符串,然后使用
    sscanf
    填充它们,就像您尝试直接使用整数和浮点一样。e、 g:

    char istr[8] = {0};
    char fstr[16] = {0};
    ...
    sscanf (line,fmt,istr,fstr);
    
    注意:您可以使用
    istr[3]
    fstr[7]
    的最小存储空间。在这种情况下,根据需要调整存储长度,但为nul终止字符提供空间)

    然后,您可以使用
    strtol
    strtof
    对每个值进行错误检查。例如:

    errno = 0;
    if ((ival = (int)strtol (istr, NULL, 10)) == 0 && errno)
        fprintf (stderr, "error: integer conversion failed.\n");
        /* underflow/overflow checks omitted */
    

    在您的示例中,将所有部分放在一起,您可以使用以下内容:

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    int main() {
    
        char *fmt = "%*4s%2[^0-9]%s";
        char *line = "xxxx  993.14";
    
        char istr[8] = {0};
        char fstr[16] = {0};
        int ival;
        float fval;
    
        sscanf (line,fmt,istr,fstr);
    
        errno = 0;
        if ((ival = (int)strtol (istr, NULL, 10)) == 0 && errno)
            fprintf (stderr, "error: integer conversion failed.\n");
            /* underflow/overflow checks omitted */
    
        errno = 0;
        if ((fval = strtof (fstr, NULL)) == 0 && errno)
            fprintf (stderr, "error: integer conversion failed.\n");
            /* nan and inf checks omitted */
    
        printf(">>>>%d|%6.2f\n",ival,fval);
    
        return 0;
    }
    

    我发布了我认为是从目前为止我得到的答案和其他来源得出的最终结论

    在Fortran中非常简单的任务在其他语言中就不那么简单了。我猜 — 不确定 — 同样的任务可以像在其他语言中的Fortran一样简单。我认为穿孔卡时代的Cobol、Pascal、PL/I和其他语言可能是微不足道的

    我认为现在的大多数语言更适合使用不同的数据结构,并从C继承了它的I/O结构。我认为Java、Python、Perl(?)和其他语言可以作为例子

    从我在这个线程中看到的情况来看,使用C读取/转换固定列长度的文本数据存在两个主要问题

    第一个问题是,正如Philip在回答中所说:“这个工具试图变得聪明而有用
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    int main() {
    
        char *fmt = "%*4s%2[^0-9]%s";
        char *line = "xxxx  993.14";
    
        char istr[8] = {0};
        char fstr[16] = {0};
        int ival;
        float fval;
    
        sscanf (line,fmt,istr,fstr);
    
        errno = 0;
        if ((ival = (int)strtol (istr, NULL, 10)) == 0 && errno)
            fprintf (stderr, "error: integer conversion failed.\n");
            /* underflow/overflow checks omitted */
    
        errno = 0;
        if ((fval = strtof (fstr, NULL)) == 0 && errno)
            fprintf (stderr, "error: integer conversion failed.\n");
            /* nan and inf checks omitted */
    
        printf(">>>>%d|%6.2f\n",ival,fval);
    
        return 0;
    }
    
    $ >>>>0|993.14
    
    char str_int[5];
    char str_float[5];
    int n = 0;
    
    sscanf(buffer, "%*4c%4[^\n]%4[^\n]%n", str_int, str_float, &n);
    if (n != 12 || buffer[n] != '\n') Fail();
    // Now convert str_int, str_float as needed.
    
    int  ival;
    float fval;
    if (strlen(buffer) != 13) Fail();
    if (sscanf(&buffer[8], "%f", &fval) != 1) Fail();
    buffer[8] = '\0';
    if (sscanf(&buffer[4], "%d", &ival) != 1) Fail();
    
    printf("xxxx%4d%4.2f\n",ival, fval);
    
    if (13 != printf("xxxx%4d%4.2f\n",ival, fval)) Fail();
    
    printf("xxxx%4d%4.2f\n",123, 9.995000001f); // "xxxx 12310.00\n"