了解sscanf格式

时间:2017-07-30 17:37:42

标签: c formatting scanf

首先,是的,这是家庭作业,是的,我一直试图自己解决这个问题,是的,我在SO上阅读了类似的问题,但没有找到我需要的帮助。

我有一个.txt文件,我正在读取一个结构,我真的很难理解sscanf的格式。我现在正处于尝试任何事情的地步,所以我担心如果我做得对,那将是因为我很幸运而不是因为我真的明白我在做什么,这就是希望你好好的人可以提供帮助。

以下是.txt

的示例数据
4, Ben Appleseed, 1587 Apple Street, Salt Lake City, UT, 80514
2, Terri Lynn Smith, 1234 Slate Street, Cincinnati, OH, 45242

注意:每个“字段”都用空格,逗号或制表符分隔。一些条目没有中间名,其他每个条目遵循相同的模式。 (如果有人有关于如何处理所有8个字段的线路的建议,我可以提供帮助)

这是我的结构:

typedef struct
{
  long lngRecordID;
  char strFirstName[50];
  char strMiddleName[50];
  char strLastName[50];
  char strStreet[100];
  char strCity[50];
  char strState[50];
  char strZipCode[50];
} udtAddressType;

这是我填写结构的常规

void AddAddressToArray(char strBuffer[], udtAddressType audtAddressList[])
{
  int intIndex = 0;

  for (intIndex = 0; intIndex < strBuffer[intIndex]; intIndex += 1)
  {

    if(sscanf(strBuffer, "%d, %s %s %s %s %s %s %s ",
        &audtAddressList[intIndex].lngRecordID,
        &audtAddressList[intIndex].strFirstName,
        &audtAddressList[intIndex].strMiddleName,
        &audtAddressList[intIndex].strLastName,
        &audtAddressList[intIndex].strStreet,
        &audtAddressList[intIndex].strCity,
        &audtAddressList[intIndex].strState,
        &audtAddressList[intIndex].strZipCode) != 8)
       {
          break;
       }
  }

}

这给了我一个输出:

  Address #50 -------------------------
     Address ID:            4
     First Name:            Ben
     Middle Name:           Appleseed,
     Last Name:             1587
     Street Address:        Apple
     City:                  Street,
     State:                 Salt
     Zip Code:              Lake

那是不对的。

我不明白如何指定我希望三个地址字段在一行上。 而我所阅读的很多内容只是让我更加困惑。

将文件加载到数组中的函数:

void PopulateAddressList( udtAddressType audtAddressList[])
{
  // Declare a file pointer
  FILE* pfilInput = 0;
  int intResultFlag = 0;
  char strBuffer[50] = "";
  char chrLetter = 0;
  int intIndex = 0;



  // Try to open the file for reading
  intResultFlag = OpenInputFile("c:\\temp\\Addresses1.txt", &pfilInput);

  // Was the file opened?
  if (intResultFlag == 1)
  {

      // Yes, read in records until end of file( EOF )
      while (feof(pfilInput) == 0)
      {
        // Read next line from file
        fgets(strBuffer, sizeof(strBuffer), pfilInput);

        AddAddressToArray(strBuffer, &audtAddressList[intIndex]);

        intIndex += 1;      
      }

    // Clean up
   fclose(pfilInput);
  }


}

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:3)

使用scansets捕获子字符串。 %200[^,],将扫描所有内容而不是逗号,最多200个字符扫描到char strSub[201];。根据需要,使用sscanf strSub来捕获字段。

if(sscanf(strBuffer, "%d, %200[^,], %99[^,], %49[^,], %49[^,],%49s",
    &audtAddressList[intIndex].lngRecordID,
    strSub,
    audtAddressList[intIndex].strStreet,
    audtAddressList[intIndex].strCity,
    audtAddressList[intIndex].strState,
    audtAddressList[intIndex].strZipCode) == 6)
{
    //sscan the fields
    fields = sscanf ( strSub, "%s%s%s",
        audtAddressList[intIndex].strFirstName,
        audtAddressList[intIndex].strMiddleName,
        audtAddressList[intIndex].strLastName);
    if ( fields == 2) {//only two fields
        //assume that the second field was for last name so copy middle to last
        strcpy (
            audtAddressList[intIndex].strLastName,
            audtAddressList[intIndex].strMiddleName);
        //set middle as blank
        audtAddressList[intIndex].strMiddleName[0] = '\0';
    }
}
else {
    break;
}

答案 1 :(得分:2)

使用feof()来控制文件循环通常是一种不好的做法。当上一个文件I / O操作设置了文件结束指示符时,feof()返回一个真值;这通常会在达到文件结束后继续循环时导致错误,但在此指示器由失败的I / O操作设置之前。 Read more about this issue here

您可以使用sscanf()格式字符串中的扫描集来实现目标。例如,scanset指令%[^,]将使sscanf()匹配任何字符,将它们存储在相应参数指示的位置,直到达到,。当该指令完成后,输入的扫描将以逗号恢复,因此可能需要在此scanset指令后面的格式字符串中放置逗号,以指示sscanf()匹配并忽略输入之前的输入中的逗号。尝试下一个分配。请注意,在%s%[]指令与scanf()系列函数一起使用时,指定最大宽度非常重要,以避免缓冲区溢出。

在将名称作为包含(可能是三个)组件的字符串获取后,如果它们存在,则该字符串可以进一步细分为名字,中间名和姓氏。

以下是使用此想法的示例。请注意,feof()的返回值代替fgets(),用于确定何时读取了文件的所有行。如果有超过MAX_RECORDS个条目,则也可以终止读取文件的循环。

首次使用sscanf()扫描文件中的行时,将检查返回值。如果没有进行六次分配,则输入不符合预期。在这种情况下,记录计数器不会递增,如果该行为空(换行作为第一个字符),则只是跳过它,否则在继续之前会打印错误消息。

成功扫描行输入缓冲区后,name[]包含记录中的全名。再次使用sscanf(),这次使用name[]作为输入字符串。存储返回值并用于确定如何存储fname[]mname[]lname[]中包含的字符串(如果适用)。

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

#define ADDR_FILE    "addresses.txt"
#define MAX_RECORDS  1000
#define BUF_SZ       1000
#define NAME_SZ      1000

struct UdtAddress_t
{
  long lngRecordID;
  char strFirstName[50];
  char strMiddleName[50];
  char strLastName[50];
  char strStreet[100];
  char strCity[50];
  char strState[50];
  char strZipCode[50];
};

int main(void)
{
    FILE *fp = fopen(ADDR_FILE, "r");
    if (fp == NULL) {
        perror("Unable to open file");
        exit(EXIT_FAILURE);
    }

    struct UdtAddress_t records[MAX_RECORDS];

    /* Populate structure */
    size_t record_ndx = 0;
    char buffer[BUF_SZ];
    while (record_ndx < MAX_RECORDS &&
           fgets(buffer, sizeof buffer, fp) != NULL) {

        char name[NAME_SZ];
        if (sscanf(buffer, "%ld, %999[^,], %99[^,], %49[^,], %49[^,], %49s",
                   &records[record_ndx].lngRecordID,
                   name,
                   records[record_ndx].strStreet,
                   records[record_ndx].strCity,
                   records[record_ndx].strState,
                   records[record_ndx].strZipCode) != 6) {

            /* Skip empty lines and bad input */
            if (buffer[0] != '\n') {
                fprintf(stderr, "bad input line\n");
            }
            continue;
        }

        /* Break name into parts */
        char fname[50];
        char mname[50];
        char lname[50];
        int scan_ret = sscanf(name, "%49s %49s %49s", fname, mname, lname);

        strcpy(records[record_ndx].strFirstName, fname);

        switch(scan_ret) {
        case 2:
            strcpy(records[record_ndx].strMiddleName, "None");
            strcpy(records[record_ndx].strLastName, mname);
            break;
        case 3:
            strcpy(records[record_ndx].strMiddleName, mname);
            strcpy(records[record_ndx].strLastName, lname);
            break;
        default:
            strcpy(records[record_ndx].strMiddleName, "None");
            strcpy(records[record_ndx].strLastName, "None");            
        }

        ++record_ndx;
    }

    /* Finished with file */
    fclose(fp);

    /* Show address information */
    for (size_t i = 0; i < record_ndx; i++) {
        printf("Address %zu -----------------------\n", i+1);
        printf("\tAddress ID:            %ld\n", records[i].lngRecordID);
        printf("\tFirst Name:            %s\n", records[i].strFirstName);
        printf("\tMiddle Name:           %s\n", records[i].strMiddleName);
        printf("\tLast Name:             %s\n", records[i].strLastName);
        printf("\tStreet Address:        %s\n", records[i].strStreet);
        printf("\tCity:                  %s\n", records[i].strCity);
        printf("\tState:                 %s\n", records[i].strState);
        printf("\tZip Code:              %s\n", records[i].strZipCode);
        putchar('\n');
    }

    return 0;
}

这是一个测试文件和一个输出示例:

4, Ben Appleseed, 1587 Apple Street, Salt Lake City, UT, 80514
2, Terri Lynn Smith, 1234 Slate Street, Cincinnati, OH, 45242
42, Cher, 4 Positive Street, Hollywood, CA, 99999
Address 1 -----------------------
    Address ID:            4
    First Name:            Ben
    Middle Name:           None
    Last Name:             Appleseed
    Street Address:        1587 Apple Street
    City:                  Salt Lake City
    State:                 UT
    Zip Code:              80514

Address 2 -----------------------
    Address ID:            2
    First Name:            Terri
    Middle Name:           Lynn
    Last Name:             Smith
    Street Address:        1234 Slate Street
    City:                  Cincinnati
    State:                 OH
    Zip Code:              45242

Address 3 -----------------------
    Address ID:            42
    First Name:            Cher
    Middle Name:           None
    Last Name:             None
    Street Address:        4 Positive Street
    City:                  Hollywood
    State:                 CA
    Zip Code:              99999