【问题标题】:fgets and strtok in a double "for" loopfgets 和 strtok 在双“for”循环中
【发布时间】:2012-03-17 14:24:03
【问题描述】:

我正在尝试将文件的内容提取到矩阵中,但文件的外观可能完全不同。

例如,所有这些文件都应该给出相同的结果:一个包含 1、2、3、4、5、6、7、8、9 的 3x3 矩阵。


1 2 3 
4 5 6
7 8 9

1  2  3 
4 5 6
7         8 9

1 2 3 4
5
6
7   8 
9

1 2 3
$something
$something else
4 5 6
$something else else
7 8 9

希望我事先知道矩阵的维度以及指示在当前过程中忽略这些行的“$”字符。

我当前使用 fscanf 的算法效果很好,但它不能用于“$something”行。

我认为我应该使用 fgets/strtok/sscanf 方法,但存在一些问题。

// File* file (already assigned)
char line[32]; //assuming 32 is enough
char* token;

fgets(line,32,file);
token = strtok(line," \t");

for (y=0; y<ySize; y++)
{
    for (x=0; x<xSize, x++)
    {
        if (token[0] == '$') //should use a str function
        {
            fgets(line,32,file);
            token = strtok(line," \t")
            x--;
        }
        else
        {
            if (we are at the end of the line)
            {
                fgets(line,32,file);
                token = strtok(line," \t")
            }
            sscanf(token,"%d",&matrix[x][y];
            token = strtok(NULL," \t");
        }
    }
}

基本上,我想在编写“如果(我们在行尾)”条件和一些关于我的方法的输入方面得到一些帮助,它完美无缺吗?我是否正确地考虑了这个过程?

谢谢。

【问题讨论】:

  • 看看strtol()。您可以使用第二个参数来设置贯穿整行的调用链。
  • cmets 是否只有完整的行,还是允许使用像 1 2 $ignore 这样的行?
  • 1 2 $ignore 是不可能的。但基本上我每次只需要调用'token == $'(仍然需要对令牌和所有内容进行一些研究)。 1 $ignore 2 也不可能。

标签: c file loops token fgets


【解决方案1】:

您应该使用getline 而不是fgets 以使事情变得更容易。后者是不可靠的。您正在寻找的测试条件是:

token == NULL;

检查this:“一旦在对 strtok 的调用中找到了 str 的终止空字符,所有后续以空指针作为第一个参数的函数调用都会返回空指针。”

【讨论】:

  • 您认为fgets 的哪些方面“不可靠”?
  • 对于以文本模式打开的流,fseek 的使用有限,因为回车换行转换可能导致 fseek 产生意外结果。唯一能保证在文本模式下打开的流上工作的 fseek 操作是: * 使用相对于任何原始值的偏移量 0 进行搜索。 * 从文件的开头查找 ftell 调用返回的偏移值。
  • 你的意思是在 x 和 y 上一段时间?会有什么区别? Ans 不需要 strtok(NULL," \t") 来处理字符串的下一个元素吗?
  • 您最初的评论是关于 fgets,但您的回复是关于 fseek 和 ftell。这表明 fgets 不可靠。
  • 我认为 fgets 与 fseek 具有相同的机制,我错了吗?
【解决方案2】:

通过使用 strspn() / strcspn() 和 sscanf()s "%n" 说明符,您可以在没有 strtok() 的情况下轻松解析。另外:原始代码中有一个“,”,其中一个“;”应该是的。

#include <stdio.h>
#include <string.h>

#define XSIZE 3
#define YSIZE 3

int matrix[XSIZE][YSIZE];

int main(void)
{
char line[200];
int pos,eat,xx,yy, rc;

xx = yy =0;
while ( fgets(line, sizeof line, stdin) ) {
    if (line[0] == '$') continue;
    for(pos=0; line[pos]; pos += eat) {
       pos += strspn(line+pos, " \t\n");
       rc =sscanf(line+pos, "%d%n", &matrix[xx][yy], &eat);
       if (rc < 1) break;
       if (++xx >= XSIZE ) {xx = 0; if(++yy >= YSIZE) goto done; }
       }   
     }
done:
        /* show it to the world ... */
for (yy = 0; yy < YSIZE; yy++) {
    for (xx = 0; xx < XSIZE; xx++) {
        fprintf (stdout, " %d", matrix[xx][yy] );
        }       
    fprintf (stdout, "\n" );
    }   
return 0;
}

【讨论】:

  • 这或多或少是我对 while 循环的想法,但我认为使用 for 循环更容易阅读,性能应该大致相同。不过为此竖起大拇指!
  • 原始代码在三个不同的地方使用了 fgets() (恕我直言,这是丑陋的)。在大多数情况下,将 fgets() 放在外部循环中会产生最干净的代码。性能不是问题,大部分时间都花在 sscanf() 内部,并且对 sscanf() 的调用次数是相同的。另外:原文没有检查 fgets() 或 sscanf() 的返回码。
  • 是的,确实,但你使用标签,我不建议这样做!
  • 如果你可以在没有 goto 的情况下重写程序,没有额外的复杂性(例如重复条件,或者更糟:指标变量):请做我的客人!
  • 嘿伙计,不要自卫,goto 是一个有效的 c 关键字,所以使用它没有任何问题。我只是表达了我的喜好。
猜你喜欢
  • 2013-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多