fgets/scanf读取文件时没有值

3
我有一个txt文件,其中包含许多行的无符号字符三元组。该数据来自OpenCV BGR图片,因此每个字节三元组都是一个BGR颜色值。
当我尝试读取文件时,使用fgets()读取的行在图片文件的大约三分之一后为空。 下面是我的代码:
    FILE* DS;
    DS = fopen("Data.txt", "r");
    char line[100];
    for (int x=0; x<image->width; x++)
    {
        for (int y=0; y<image->height; y++)
        {
            fgets(line, 10, DS);
            sscanf(line, "%c %c %c", &FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 0], 
                                     &FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 1],
                                     &FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 2]);
        }
    }
    fclose(DS);

我确定每行都填满了三个字符,因为我进入文件并查看了第x*y行。然而,在文件的三分之一后,我的行中只有一个空白字符。
希望这足够清楚了。 提前感谢。
编辑:
文本文件的一部分:
Z a `
Y ^ a
Z ` a
Y ^ a
Y _ `
Z ` a
Y a `
Z b a
V c a
X b a
V c a
V c a
V c a
V c a
T c a
T c a
S c a
S c a
R b `
R b `
U b `
W a `
W a `
Y a `
Z b a
[ b a
Z b a
[ c b
Y c b
Y c b

这是被以下人员写入文件的:

for (int x=0; x<image->width; x++)
    {
        for (int y=0; y<image->height; y++)
        {
            fprintf(DS, "%c %c %c\n",   FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 0], 
                                        FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 1], 
                                        FrontTexture->imageData[FrontTexture->widthStep * y + x * 3 + 2]);
        }
    }

编辑2:

这是我的文本文件: http://www.2shared.com/document/SmLbhYzH/Datensatz.html 大小:6.15MB。

编辑3:

我的OpenCV图像的图像数据只是一个字符数组,应该填充为b0 g0 r0 b1 g1 r1 ...

定义如下: char *imageData;


只是确认我是否理解正确 - 该文件有x*y行,每行都有三个数字吗? - SingerOfTheFall
这真的是 C++ 吗?如果不是的话,最好去掉标签!你不会想分享宽度和高度的值,并将 Data.txt 文件的内容放在我们可以看到的地方吗? - Rook
你确定这个文件的三元组是作为文本,而不是二进制值存储的吗?因为你读取它们的方式像是读取文本一样... - rodrigo
这是一个包含无符号字符的.txt文件。这意味着您可以使用编辑器打开文件并查看字母。宽度和高度:640 * 480。我正在读取的文件部分肯定只包含单个字符。 - Martin Hennig
1
对于这样的固定格式,我建议一次读取六个字符(三个数据,三个空格),并且不使用scanf和fgets进行解析,因为它们只会给你带来麻烦。 - Jonas Schäfer
显示剩余6条评论
4个回答

2
使用与您非常相似的代码库,我能够读取整个文件:
unsigned char a, b, c;

DS = fopen("/home/mike/win_share/Datensatz.txt", "r");
char line[100];
while(fgets(line, 10, DS) != NULL)
{
    sscanf(line, "%c %c %c", &a, &b, &c);
    printf("%c (%d) %c (%d) %c (%d)\n", a, a, b, b, c, c);
}

我看到你说:在文件的三分之一后,我的行中只有一个空白字符,然后我想知道...

你是通过查看文件来验证字符是否被正确读取的吗?你知道里面有不可显示的字符对吧?

x (120) o (111) m (109)
{ (123) t (116) s (115)  <-- I'm guessing this is the last line that looks OK
  (127) u (117) w (119)  <-- (127) DEL char won't show
� (129) z (122) | (124)
� (131)   (127) � (128)

再想一想...你的数组索引访问是否正确?我不确定widthStep设置为什么,但这可能会导致问题:

如果FrontTexture->widthStep == 1,并且x == 0y == 0

[1 * 0 + 0 * 3 + 0] => [0 + 0 + 0] => [0]
[1 * 0 + 0 * 3 + 1] => [0 + 0 + 1] => [1]
[1 * 0 + 0 * 3 + 2] => [0 + 0 + 2] => [2]

然后在下一次迭代中:如果 FrontTexture->widthStep == 1,且 x == 0y == 1

[1 * 1 + 0 * 3 + 0] => [1 + 0 + 0] => [1]  // Overwrite the data in imageData[1]
[1 * 1 + 0 * 3 + 1] => [1 + 0 + 1] => [2]  // Overwrite the data in imageData[2]
[1 * 1 + 0 * 3 + 2] => [1 + 0 + 2] => [3]

你尝试过打印出一些步骤来验证一切是否按照你的预期工作吗?


我正在填充的纹理在1/3之后只包含空格,因为一些字符无法正确读取。是这样吗?所以你的意思是我也可以将它们读取为整数(%d),然后稍后再将它们作为字节处理? - Martin Hennig
@MartinHennig - 我猜是这样,但我需要看看你的FrontTexture结构才能确定它是否没有被正确地_存储_。我的重点在于通过fgets()sscanf()以简单的方式(如我的示例)读取数据是可以正常工作的。因此,您读取数据的方式是正确的,但其他方面可能有问题。您能否发布您的结构以及为其分配/初始化的任何内容? - Mike
@MartinHennig - 我尝试了Windows和Linux,VS10和gcc,每次都能正确读取数据。我在我的答案中更新了一些调试建议,你是否已经验证了每个迭代中循环将数据存储在正确的位置? - Mike
widthStep 表示一条纹理线的完整宽度,在我的情况下是 1920。我认为索引没问题。我会尝试像你那样将我的行值放入单个无符号字符变量中,并进行报告。谢谢你的时间! - Martin Hennig
新信息:当line =“”时,fgets返回NULL,因此不能读取任何字符。请测试是否在您的测试中fgets从不返回NULL! - Martin Hennig
显示剩余4条评论

2

你需要检查这些函数的返回值。参见fgetssscanf。这些返回值很重要,会告诉你问题出在哪里。


这是一个非常好的观点(大多数人确实忽略了这些),但我想它应该是一条注释... - SingerOfTheFall
@SingerOfTheFall- 这是解决文件格式错误的问题的方案。此外,使用返回值是良好的编程实践 - 编写规范/代码的人放置它们是有原因的。 - Ed Heal

1

使用:

sscanf(line, " %c %c %c", ...

请注意字符串开头的空格。这将避免将空白字符读取为有效的第一个字符。

OP的问题不是fgets之后无法继续从文件中获取数据吗?我有点困惑 :S - SingerOfTheFall
从描述中不是很清楚...也许...第二次阅读后,我现在认为该文件是二进制的,他正在将其读取为文本。 - rodrigo
我认为这不是二进制文件,因为原帖中说“我进入了文件并查看了x*y行”……而且文件名是“Data.txt”,这对我来说意味着该文件应该以文本模式创建和访问…… - SingerOfTheFall
你假设“空白”不是一个有效的字符,这是一个错误的假设!每行的第一个字符是BGR像素中蓝色通道的值;它可以在0到255之间。即使是非打印字符在这里也是完全有效的。 - Rook

1

我们确实需要看到输入文件的样本。然而,存在一些问题。

fgets 最多只会读取 10 个字符,如果一行有 11 个字符,则第一次调用将读取其中的 10 个字符,第二次调用将读取剩余的一个字符。 您还应该测试 fgets 返回的内容,并确保 sscanf 返回了 3(它匹配的事物数量)以提高安全性。 直接调用 fscanf 可能更好,避免使用 fgets。

fscanf(DS, "%c %c %d", ...

因为这样可以更好地处理空格。


看起来行必须小于10个字符,因为sscanf调用只匹配了5个字节的内容。不过,我猜我们需要查看输入代码,才能确定是否存在明显的逻辑错误。 - Rook
我认为如果你仅使用fscanf,可能会遇到fscanf跳过空格的问题,正如manpage中所示:一系列的空白字符(空格、制表符、换行符等;参见isspace(3))。该指令匹配输入中任意数量的空格,包括零个。 - Jonas Schäfer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接