使用fgets()读取多行文本,如何进入下一行?

5

我正在设计一款纸牌游戏,并打算使用一个包含卡牌数据的文件。每行都包含104个字符,等同于一副牌。

我使用了char ****,因为:

  1. 有多副牌
  2. 有4名玩家
  3. 每副牌有13张牌
  4. 每张牌用两个字符表示,例如9H代表红桃九,3D代表方块三。

我想使用fgets()读取多行,但不确定是否可行...

for循环是按照牌堆文件设置的,我只是想知道当fgets遇到\n时是否会跳到下一行...

    di->cards = (char ****)malloc(sizeof(char***) * di->numDecks);
    for (i = 0; i < di->numDecks; i++) {
        di->cards[i] = (char ***)malloc(sizeof(char**) * 4);
        for (j = 0; j < 4, j++) {
            di->cards[i][j] = (char **)malloc(sizeof(char*) * 13);
            for (k = 0, k < 13, k++) {
                di->cards[i][j][k] = (char *)malloc(sizeof(char) * 3);
            }
        }
    }

    for (i = 0; i < di->numDecks, i++) {
        for (j = 0; j < 13, j++) {
            for (k = 0; k < 4; k++) {
                while ((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
            }
        }
    }

1
你试过了吗?看看它是否有效? - ravenspoint
5
这太过复杂了,用那么多层指针间接引用来表达一个简单的问题实在是太过了。此外,在C语言中不要对malloc()的返回值进行强制类型转换。 - unwind
8
哇,一位四星级程序员! - wildplasser
1
循环分配内存总是与循环释放内存相匹配。 - ryyker
在C语言中,永远不要对malloc的返回值进行强制转换。 - harper
4个回答

6

fgets()通常在循环中调用,例如:

FILE *fp;
char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
fp = fopen("C:\\somedir\\card.txt", "r");
while(fgets(buf, sizeof(buf), fp)) //where sizeof(buf) is the length of   
                                   //line you anticipate reading in.
{
    //do something with buf here;
    //The input of fgets will be NULL as soon 
    //as its input fp has been fully read, then exit the loop  
}
fclose(fp);  

你的语句 while((fgets(cards[i][k][j], 3, di->deckFile)) != NULL);
有几个问题,其中一个是末尾的;。它只会在这一行上循环,不会给你机会在读取下一行之前对已读取的行进行任何操作。此外,3可能不是你想要读取的行的长度,对吗?3是用于保存卡片数据的缓冲区大小,但从文件中读取的可能更长。

所以,除了这些问题,还要考虑评论中提到的其他建议,并根据指示进行修改。

[编辑] 修改为读取一个包含"AS3D4C...(52张牌)"的文件,共4行
它将填充足够的空间来存放4副牌。你可以使用这个来
查看如何读取数据。之前使用的strtok只适用于有分隔符的情况,如果可以的话,我建议使用分隔符而不是长字符串。希望这能帮到你。

(注意,我在这个例子中既没有使用malloc()也没有使用calloc()。)
#include <ansi_c.h>
#define FILENAME "C:\\dev\\play\\cards.txt"

int main()
{
    int i, j;    
    FILE *fp;
    char buf[260];// or char *buf, then use malloc - make index size appropriate length for anticipated line len.
    char *cardTok;
    char cards[208][3]; //4 players, 4 decks, each card is 3 bytes (eg. [A|S|\0], they all need a null termination)
    
    memset(cards, 0, 208*3);
    
    fp = fopen(FILENAME, "r");
    j = 0;
    while(fgets(buf, sizeof(buf), fp)) //where buf len is initialized at 260   
                                       //and well over the anticipated 104/line, including \n etc.
    {                          //note, fgets() will read up to n-1 characters and place a \0 at the end
                           //but will stop reading sooner if it sees end of line.
        for(i=0;i<52;i++) //i is card number
        {
            cards[i+j][0] = buf[2*i+0]; 
            cards[i+j][1] = buf[2*i+1];
            cards[i+j][2] = 0;
        }
        j+=52;
    }
    fclose(fp);
}  

我的文本文件看起来是这样的:

9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQhKD
6C9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
2D9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4SQh
3S9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H9H3D4SQhKD1H1H9H3D4SQhKD1H9H3D4SQhKD3D4SQhKD1H9H3D4SQhKDKD1H9H3D4S

1
你不想要"//where 259 is the length of line with its \n you anticipate"吗?一个大小为260的缓冲区可以很好地读取258个字符,加上\n需要1个字符来存储\0。 - chux - Reinstate Monica
1
@ryyker 在这里没问题,但请注意:在过去的问题中,代码使用了像 fgets(buf, 120, fp) 这样的东西,但 buf 的大小后来变成了 < 120,而 fgets() 没有得到更新。因此,我推荐使用 fgets(buf, sizeof buf, fp) - chux - Reinstate Monica
1
@chux 如果 buf 是动态分配的呢? - This isn't my real name
1
@ElchononEdelson - 这取决于 malloc() 的初始化器,不是吗?例如:char *buf; buf = malloc(260);,然后 while(fgets(buf, 260, fp);。我相信 @chux 只是想确保我和 OP 理解了在不改变 fputs() 的中间参数的情况下更改变量长度的危险,并提供缓解方法。 - ryyker
2
@ryyker @ElchononEdelson 例如:size_t Len; buf = malloc(Len); fgets(buf, Len, stdin); 无论如何,用于设置 buf 内存的值,无论是通过 buf[Len] 还是 buf = malloc(Len),都只定义一次,例如 Len = 120;#define Len (120),然后在声明 buffgets(buf, Len, ...) 调用中使用。避免在 fgets() 中放置魔法数字。 - chux - Reinstate Monica
显示剩余6条评论

3
    #include <stdio.h>
    char *fgets(char *s, int size, FILE *stream);

fgets()函数从流中读取最多size-1个字符,并将它们存储到指向缓冲区s的指针中。当遇到EOF换行符时,读取停止。

需要注意的是:如果读取到换行符,它也会被存储到缓冲区中。在缓冲区的最后一个字符后面存储了一个终止空字节('\0')。

如果要比较行,请在空字节之前删除\n

如果您想读取单行,则可以使用此函数。

char line[100]; // here you can use char *line=malloc(100);

fgets(line,sizeof line,file_stream);
printf("%s\n",line);

如果您想读取多行:

如果您想要读取多行文本:

char lines[20][100]; // here you can use char **lines=malloc(100);
i=0;
//if you use **lines allocate size for all lines with the loop or else you can allocate size inside loop and then read.
while((fgets(lines[i],SIZE_ALLOCATED_FOR_LINE,file_stream)!=NULL) && (i<20))
    {
    printf("%s\n",line[i++]);
    }

2

这份文档中提到:

char *fgets( char          *str, int count, FILE          *stream );
char *fgets( char *restrict str, int count, FILE *restrict stream );

从给定的文件流中读取最多count - 1个字符,并将它们存储在str中。生成的字符串始终以NULL结尾。如果遇到文件末尾或找到换行符,则停止解析,此时str将包含该换行符。
如果操作失败,则返回NULL
如果失败是由于EOF条件引起的,则还会在stdin上设置eof指示器(请参见feof())。如果失败是由于其他错误引起的,则在stdin上设置错误指示器(请参见ferror())。
此外,请检查feof以确保获取NULL是由于EOF。

0
如果你想将fgets输入的内容全部输入到一个数组或字符串数组中,你该怎么做呢?我尝试了不同的方法,但总是出现段错误。

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