2

我正在尝试读取文件并将每个字符替换为 ASCII 表中对应的字符。它正确打开文件,但继续读取第一个字符。

int main(int argc, char * argv[])
{
    FILE *input;
    input = fopen(argv[2], "r+");
    if (!input)
    {
        fprintf(stderr, "Unable to open file %s", argv[2]);
        return -1;
    }

    char ch;
    fpos_t * pos;
    while( (ch = fgetc(input)) != EOF)
    {
            printf("%c\n",ch);
            fgetpos (input, pos);
            fsetpos(input, pos-1);
            fputc(ch+1, input);
    }
    fclose(input);
    return 1;
}

文本文件是

abc
def
ghi

我很确定这是由于 fgetpos 和 fsetpos 造成的,但如果我删除它,它会在文件末尾添加字符,下一个 fgetc 将返回 EOF 并退出。

4

5 回答 5

3

处理以更新模式打开的文件时必须小心。

C11 (n1570), § 7.21.5.3fopen函数

当以更新模式打开文件时('+'作为上述模式参数值列表中的第二个或第三个字符),可以在关联的流上执行输入和输出。

但是,如果没有对函数fflush或文件定位函数(fseekfsetposrewind输入操作遇到文件结尾。

所以你的阅读可能看起来像:

int c;

while ((c = getc(input)) != EOF)
{
    fsetpos(/* ... */);
    putc(c + 1, input);
    fflush(input);
}

顺便说一句,你会遇到'z'性格问题。

于 2013-04-27T16:46:08.730 回答
2

用于执行随机访问的过程,例如

  1. 定位记录
  2. 阅读记录
  3. 定位记录
  4. 更新(写入)记录
  5. 做刷新(完成更新)

以下代码是考虑到它的重写。

#include <stdio.h>
#include <ctype.h>

int main(int argc, char * argv[]){
    FILE *input;
    input = fopen(argv[1], "rb+");
    if (!input){
        fprintf(stderr, "Unable to open file %s", argv[1]);
        return -1;
    }

    int ch;
    fpos_t pos, pos_end;
    fgetpos(input, &pos);
    fseek(input, 0L, SEEK_END);
    fgetpos(input, &pos_end);
    rewind(input);
    while(pos != pos_end){
        ch=fgetc(input);
        if(EOF==ch)break;
        printf("%c",ch);
        if(!iscntrl(ch) && !iscntrl(ch+1)){
            fsetpos(input, &pos);
            fputc(ch+1, input);
            fflush(input);
        }
        pos += 1;
        fsetpos(input, &pos);
    }
    fclose(input);
    return 1;
}
于 2013-04-27T18:35:39.700 回答
2

7.21.9.1p2

fgetpos 函数将stream 指向的流的解析状态(如果有)和文件位置指示符的当前值存储在pos 指向的对象中。存储的值包含 fsetpos 函数可用于将流重新定位到调用 fgetpos 函数时的位置的未指定信息。

未指明的信息这些词似乎并没有激发人们对这种减法的信心。您是否考虑过在读取字符fgetpos 之前调用,这样您就不必进行非便携式减法?此外,您的调用fgetpos可能应该将指针传递给现有的 fpos_t(例如使用&address-of运算符)。您的代码当前传递了一个指向gibberish的指针。

fgetc返回一个int,以便它可以表示unsigned char与负值不同的每个可能EOF值。

假设您的char默认值为无符号类型。(ch = fgetc(input))将(可能为负,对应于错误)返回值直接转换为您的无符号char类型。可以(unsigned char) EOF比较等于EOF吗?你的循环什么时候结束?

假设您的char默认设置为signed type. (c = fgetc(input))可能会将任何返回值的较高范围unsigned char转换为负数(尽管从技术上讲,此语句会调用未定义的行为)。在某些情况下,您的循环不会过早结束(例如在EOF 之前)吗?

这两个问题的答案表明您fgetc错误地处理了返回值。将其存储在int!

也许您的循环应该类似于:

for (;;) {
    fpos_t p;

    /* TODO: Handle fgetpos failure */
    assert(fgetpos(input, &p) == 0);

    int c = fgetc(input);

    /* TODO: Handle fgetc failure */
    assert(c >= 0);

    /* TODO: Handle fsetpos failure */
    assert(fsetpos(input, &p) == 0);

    /* TODO: Handle fputc failure */
    assert(fputc(c + 1, input) != EOF);

    /* TODO: Handle fflush failure (Thank Kirilenko for this one) */
    assert(fflush(input) == 0);
}

确保检查返回值...

于 2013-04-27T17:07:10.593 回答
2

我真的怀疑问题出在这里:

fpos_t * pos;

您正在声明一个指向 a 的指针,fpos_t这很好,但是,当您检索 pos 时,信息存储在哪里?

它应该是:

fpos_t pos; // No pointer
  ...
     fgetpos (input, &pos);
     fsetpos(input, &pos);  // You can only come back where you were!

阅读(草案)标准,唯一的要求fpos_t是能够代表 a 的位置和状态FILE,似乎没有办法移动位置。

请注意,表达式pos+1移动指针,不会影响它指向的值!

你可能想要的是旧的,亲爱的ftell()fseek()这会让你四处走动。只需记住"rb+"flush()您的fputc().

当您解决了这个基本问题时,您会注意到您的方法还有另一个问题:处理换行符!您很可能应该限制您将应用“增量”的字符范围,并规定afollowzAfollow Z

也就是说,是否需要就地进行操作?

于 2013-04-27T16:45:59.897 回答
1

更新模式('+')可能有点难以处理。也许您可以更改方法并将整个文件加载到 char 数组中,对其进行迭代,然后最终将整个内容写入一个空的输入文件?没有流问题。

于 2013-04-27T17:01:15.617 回答