【问题标题】:Reading string with spaces from file in c从c中的文件中读取带空格的字符串
【发布时间】:2014-05-15 13:52:11
【问题描述】:

我有一个关于从文件中读取包含空格的字符串的问题。 这是我的函数,它从文件中读取数据并将其写入结构数组。 但是,如果第一个字符串的输入包含两个字符串和它们之间的空格,则第一个字符串转到第一个参数,第二个字符串转到第二个参数。 如何以必要的数据进入指定的结构字段的方式从文件中拆分行? 谢谢!

void readFileToDiary(FILE* file, drivingDiary* diaryTemp){

    int i = 0;

    file = fopen("test.txt", "r");
    if (file == NULL)
        printf("\nError opening file!\n");
    else{
        while (!feof(file)){
            fscanf(file, "%s %s %d %s %s %d", diaryTemp[i].locationStart,
                diaryTemp[i].timeStart, &diaryTemp[i].odometerStart,
                diaryTemp[i].locationEnd, diaryTemp[i].timeEnd,
                &diaryTemp[i].odometerEnd);
            i++;
        }
    }
}

示例 文件内的行包含: 某地 13:40 10000 某地 14:45 10120

所以,

      "some place" -> first field of structure,
             13:40 -> second field,
             10000 -> third field,
"some other place" -> fourth field,
             14:45 -> fifth filed,
             10120 -> sixth field.

UPD 工作版本在这里(没有 fscanf() 检查)!

void readFileToDiary(FILE* file, drivingDiary* diary){

    int i = 0;

    file = fopen("test.txt", "r");
    if (file == NULL)
        printf("\nError opening file!\n");
    else{
        while(fscanf(file, " %[a-zA-Z ]%[0-9:]%d %[a-zA-Z ]%[0-9:]%d%*[\n]",
                diary[i].locationStart,
                diary[i].timeStart,
                &diary[i].odometerStart,
                diary[i].locationEnd,
                diary[i].timeEnd,
                &diary[i].odometerEnd) != EOF)
            i++;        
        }
    }
}

【问题讨论】:

  • 使用getLinefgets 从文件中获取整行而不是使用strtok 让每个字段用空格分隔怎么样?
  • 使用fgets()/sscanf()char buf[100]; while(fgets(buf, sizeof buf, stdin) != NULL)) { if (sscanf(buf, "%s %s %d %s %s %d", ...) != 6) Handle_FormatError(); ... }
  • 基本问题是您松散地使用了 string 的 4 个定义。 1) 以'\n' 结尾的文本文件行,2) 用“它们之间的空格”分隔的字符分组,3) 使用"%s" 扫描由white-space分隔的char 组>(不仅是' ')并试图将这三个保存为 4)C 字符串,char 的数组终止并包括一个'\0'。建议更明确地说明您的目标或提供一些示例。
  • 可能是双通来确定找到多少令牌。如果它大于字段编号,那么你连接两个连续的标记来填充一个字段。但是如果使用了多个空格的字符串,那就麻烦了。
  • 可能在上下文 " %[a-zA-Z ]%[0-9:]%d %[a-zA-Z ]%[0-9:]%d" 上是分开的,但在 locationStartlocationEnd 中会有尾随空格。一定要测试fscanf() 结果。代码肯定需要使用除 fscanf() 之外的其他内容来完全满足您的目标。

标签: c string file parsing file-io


【解决方案1】:

由于开始和结束位置的空格数未知,因此使用 scanf() 方法会很复杂。但是,可以使用更“老派”的方法对其进行解析....

"some place 13:40 10000 some other place 14:45 10120"

首先,将每条线视为包含两个“路点”;一个“开始”和“结束”。然后通过使用单一的“路点”解析方法简化解析单个路点。

"some place 13:40 10000", "some other place 14:45 10120"

因此,有几种方法可以构建“路点”解析器。当我看到这个问题时,我不想一开始就使用空格字符作为分隔符。我想找一些其他的起点。

每个路点都包含一个“:”字符,所以我从那里开始。从 ':' 字符我向左走,找到空格,并将空格转换为 '\0' 字符串终止字符。这隔离了航路点的“位置”。

然后,从':',我向右走,找到空格,并将空格转换为'\0'字符串终止字符。这隔离了航路点的“时间”。

使用 strtoul() 可以轻松隔离航点的里程计部分。

我的代码如下:

/***************************************************************************
** Compiler setup
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

typedef struct DRIVING_WAYPOINT_S
   {
   char *location;
   char *time;
   long  odometer;
   } DRIVING_WAYPOINT_T;

typedef struct DRIVING_DIARY_S
   {
   DRIVING_WAYPOINT_T start;
   DRIVING_WAYPOINT_T end;
   } DRIVING_DIARY_T;

/***************************************************************************
** Parse a waypoint.
*/
int ParseRecord(
      char                *I__string,
      char               **_O_string,
      DRIVING_WAYPOINT_T  *I__waypoint
      )
   {
   int rCode=0;
   char *cp, *space;

   /* Parse location */
   cp=strchr(I__string, ':'); /* Find the first ocurrance of ':' */
   if(NULL == cp)
      {
      rCode=EINVAL;
      fprintf(stderr, "Parse error.  Time does not contain a ':'\n");
      goto CLEANUP;
      }

   space = cp;
   while(' ' != *space)
      --space;

   *space = '\0';
   I__waypoint->location = strdup(I__string);

   /* Parse time */
   cp = space + 1;
   space=strchr(cp, ' ');
   if(NULL == space)
      {
      rCode=EINVAL;
      fprintf(stderr, "Parse error.  No space following time\n");
      goto CLEANUP;
      }

   *space='\0';
   I__waypoint->time = strdup(cp);

   /* Parse odometer */
   cp = space+1;
   I__waypoint->odometer = strtol(cp, &cp, 10);
   while(' ' == *cp)
      ++cp;

   if(_O_string)
      *_O_string = cp;

CLEANUP:

   return(rCode);
   }

/*******************************************************************************
** Parse the diary file. 
*/
int ReadFileToDiary(
      FILE             *I__fp,
      DRIVING_DIARY_T **IO_diary,
      int              *IO_diaryEntries
      )
   {
   int rCode = 0;
   char line[255+1];

   for(;;)
      {
      DRIVING_DIARY_T *tmp;
      char *cp;

      /* Read the next line from the file. */
      errno=0;
      if(NULL == fgets(line, sizeof(line), I__fp))
         {
         if(feof(I__fp))
            break;

         rCode=errno;
         fprintf(stderr, "fgets() reports: %d.\n", errno);
         goto CLEANUP;
         }

      /* Expand the diary array for one more entry. */
      tmp=realloc(*IO_diary, ((*IO_diaryEntries)+1) * sizeof(DRIVING_DIARY_T));
      if(NULL == tmp)
         {
         rCode=ENOMEM;
         fprintf(stderr, "realloc() failed.\n");
         goto CLEANUP;
         }
      *IO_diary = tmp;
      memset(&(*IO_diary)[*IO_diaryEntries], '\0', sizeof(DRIVING_DIARY_T));

      /* Check for empty string. */
      if('\0' == *line)
         continue;

      /* Parse the 'start' waypoint. */
      rCode=ParseRecord(line, &cp, &(*IO_diary)[*IO_diaryEntries].start);
      if(rCode)
         {
         fprintf(stderr, "ParseRecord(start) reports: %d\n", rCode);
         goto CLEANUP;
         }

      /* Parse the 'end' waypoint. */
      rCode=ParseRecord(cp, NULL, &(*IO_diary)[*IO_diaryEntries].end);
      if(rCode)
         {
         fprintf(stderr, "ParseRecord(end) reports: %d\n", rCode);
         goto CLEANUP;
         }

      /* Increment the 'diary entries' counter. */
      (*IO_diaryEntries)++;
      }

CLEANUP:

   return(rCode);   
   }

/*******************************************************************************
** Free the diary array.
*/
int DiaryFree(
      DRIVING_DIARY_T *diary,
      int              diaryEntries
      )
   {
   int rCode=0;
   int nCnt;

   for(nCnt=0; nCnt<diaryEntries; ++nCnt)
      {
      free(diary[nCnt].start.location);
      free(diary[nCnt].end.location);
      free(diary[nCnt].start.time);
      free(diary[nCnt].end.time);         
     }

   free(diary);   

   return(rCode);
   }

/*******************************************************************************
** Program start.  
*/
int main()
   {
   int              rCode        = 0;
   FILE            *fp           = NULL;
   DRIVING_DIARY_T *diary        = NULL;
   int              diaryEntries = 0;
   int              nCnt;

   /* Open the data file. */
   errno=0;
   fp = fopen("test.txt", "r");
   if(NULL == fp)
      {
      rCode=errno;   
      fprintf(stderr, "fopen() failed.  errno:%d\n", errno);
      goto CLEANUP;
      }

    /* Parse the file into the dynamic diary array. */
    rCode=ReadFileToDiary(fp, &diary, &diaryEntries);
    if(rCode)
      {
      fprintf(stderr, "ReadFileToDiary() reports: %d\n", rCode);
      goto CLEANUP;
      }

    /* Print out the array. */
    for(nCnt=0; nCnt < diaryEntries; ++nCnt)
      {
      printf("[%d] %s %s %ld %s %s %ld\n",
         nCnt,
         diary[nCnt].start.location,
         diary[nCnt].start.time,
         diary[nCnt].start.odometer,
         diary[nCnt].end.location,
         diary[nCnt].end.time,
         diary[nCnt].end.odometer
         );
       }

CLEANUP:

   if(diary)
      DiaryFree(diary, diaryEntries);

   if(fp)
      fclose(fp);

   return 0;
   }

【讨论】:

  • 这里有很多好的代码。 1) 为什么while (!feof(I__fp)) {?为什么不while (1) { ... if (NULL == fgets(line, sizeof(line), I__fp)) { if (feof(I__fp)) break;。 2) 建议将realloc() 移动到 成功的fgets()? 3)while(' ' != *space) --space;是个问题,前面没有空格。
  • @chux,感谢您的评论。对于您的 1) 和 2) 点,您是正确的。也许我过分关注解析方面;而不是文件行的读取,或者数组大小的效率。但是,我编辑了代码并解决了这两点。
  • @chux,至于 3),我相信在 ':' 字符之前(很可能)有一个空格(即:"some place 13:40 10000")在 'place' 之间和“13”。
  • 同意前面可能的空格。其余代码具有良好的错误检查功能,而且这种检查似乎是合理的,而不是迂腐。为出色的工作 +1
  • 嗯,到目前为止对我来说似乎很复杂......有人检查过它是否工作吗?如果是这样,我可能应该将此答案标记为问题的解决方案。
猜你喜欢
  • 1970-01-01
  • 2020-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-06
相关资源
最近更新 更多