在C中阅读.txt文件中的空行

3xiyfsfu  于 2023-02-03  发布在  其他
关注(0)|答案(1)|浏览(161)

我目前正在为我的第一个编程课程做一个项目。我们正在构建一个C程序,该程序读取一个.txt文件,其中包含有关奥塞洛(又名翻转)比赛的信息,并确定比赛是否正确(如果它遵循规则,移动是否正确等)。收到的.txt文件的样式如下:

FirstName,Letter1
FirstName,Letter2
Letter
Plays

其中字母1和字母2为B或N(分别代表西班牙语中的白色和黑色),那么接下来的一行又是B或N,这决定了谁开始移动(在我们的奥塞洛版本中,两种颜色都可以开始),然后播放是8 × 8网格上的一系列位置,由表示,字母从A到H,数字从1到8。每行有一个播放,因此(不完全)匹配示例可以是

John,N
Martin,B
N
F5
D6
C3

第一步是约翰,他玩黑色F5。
我的问题是一个玩家跳过一个回合的可能性。玩家可以(也必须)跳过他们的回合,当且仅当没有可能的发挥。所以,如果一个玩家跳过一个回合,一个空行将取代他们的发挥。因此,可能有一个.txt文件与以下风格(这是一个不可能的举动,以防万一)

F5
D6

C3

其中D 6和C3都是由同一个玩家连续打出的,因为他们的对手跳过了他们的回合。
所以,在我的程序的某一部分,我有一个read_file函数,它会遍历.txt文件,并将游戏信息保存在我称为“datos”的结构体上。特别是,它的一个字段是plays,这是一个字符串数组。在那里,我存储了正常游戏格式的字符串和跳过的回合的“XX”。所以最后一场游戏的数组将是[“F5”,“第六类”、“第二十类”、“第三类”]。
我花了2天时间在这个函数上,当它最终看起来工作时,我意识到它根本没有保存跳过的回合。相反,当一个玩家跳过它的回合时,这个函数停止保存所做的比赛并继续程序,留下比赛不完整。这是我目前的代码,它甚至没有尝试存储“XX”的戏剧,因为我已经尝试了一千种方法,他们总是失败。

char play[3];
    while (fscanf(file, "%s\n", play) == 1) {
       if (strlen(play) != 2 || jugada[0] < 'A' || jugada[0] > 'H' || jugada[1] < '1' || jugada[1] > '8') {
            fclose(file);
            free_datos(match);
            return 5;
        }

        //if (jugada[0] == '\n' && jugada[1] == '\0') strcpy(jugada, "XX");

        match->plays_counter++;
        match->plays = realloc(match->plays, match->plays_counter * sizeof(char *));
        match->plays[match->plays_counter - 1] = malloc(MAX_PLAY * sizeof(char));
        strcpy(match->plays[match->plays_counter - 1], play);
    }

当前版本只是在发现空行时结束while循环,这不是我需要它做的事情。
我尝试了多个版本,使用fscanf,fgets,chatGPT,搜索stackOverflow,没有找到我的问题的答案。我读过建议的帖子,但大多数都是处理删除空行,而不是真正想要考虑它们。
关于其他版本,有时他们会在每次播放后添加一个“XX”,使数组的大小加倍(但正确识别跳过的回合,哈哈),更经常的是,他们会在找到跳过的回合后崩溃或结束while循环,等等。我问了chatGPT太多次,它一直给我错误的答案。
有人能帮我吗?我试过fgets,但我试过的所有版本都不能用。由于某种原因,我在youtube或任何地方都找不到更多的信息,大多数时候处理空行时该怎么做被忽略了。
对不起,如果这是太长的时间,我已经在这方面工作了令人惊讶的大量时间考虑到它应该真的不是那么难。它是驾驶我疯了在这一点上。谢谢你提前!

e0bqpujr

e0bqpujr1#

你的问题是函数调用

fscanf(file, "%s\n", play)

并不符合你的意愿。
%s说明符将首先消耗并丢弃所有whitespace characters(包括空格和换行符),然后当它遇到非空白字符时,它将读取遇到的所有内容,直到下一个空白字符。
格式字符串中的\n字符将指示scanf使用并丢弃所有空格字符(而不仅仅是单个!),直到遇到非空格字符。
这意味着通过输入

F5
D6

C3

fscanf的第一次调用将读取F5\n,对fscanf的第二次调用将读取D6\n\n,而第三次函数调用将读取C3\n。然而,这不是您想要的。您想要第二次调用仅读取一行而不是两行,即您想要它仅读取D6\n而不是D6\n\n
我不建议你尝试用scanf来解决这个问题,相反,我建议你使用fgets,因为该函数的行为方式更直观,特别是,该函数总是一次读取一行(除非提供的内存缓冲区不够大,无法存储整行)。
下面是一个示例程序:

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

#define MAX_MOVES 64

int main( void )
{
    char moves[MAX_MOVES][3];
    int num_moves = 0;

    FILE *fp;
    char line[4];

    //attempt to open file
    fp = fopen( "input.txt", "r" );
    if ( fp == NULL )
    {
        fprintf( stderr, "Error opening file!\n" );
        exit( EXIT_FAILURE );
    }

    //read moves from stream
    while ( fgets( line, sizeof line, fp ) != NULL )
    {
        //remove newline character, if it exists
        line[strcspn(line,"\n")] = '\0';

        //make sure that we have room for another move
        if ( num_moves == MAX_MOVES )
        {
            fprintf( stderr, "Error: Not enough room for storing more moves!\n" );
            exit( EXIT_FAILURE );
        }

        switch ( strlen( line ) )
        {
            case 0:
                //this is a skipped move
                strcpy( moves[num_moves++], "XX" );
                break;

            case 2:
                //this is not a skipped move
                if (
                    line[0] < 'A' || line[0] > 'H' ||
                    line[1] < '1' || line[1] > '8'
                )
                {
                    fprintf( stderr, "Error: Invalid move!\n" );
                    exit( EXIT_FAILURE );
                }
                strcpy( moves[num_moves++], line );
                break;

            default:
                fprintf( stderr, "Error: Move has invalid length!\n" );
                exit( EXIT_FAILURE );
        }
    }

    //print all moves
    for ( int i = 0; i < num_moves; i++ )
    {
        printf( "%s\n", moves[i] );
    }

    //cleanup
    fclose( fp );
}

对于输入

F5
D6

C3

该程序具有以下输出:

F5
D6
XX
C3

相关问题