2017-07-30 45 views
0

首先要做的是,这是作业,是的,我一直试图弄清楚自己,是的,我读过类似的问题,但没有找到我需要的帮助。了解sscanf格式

我有一个.txt文件,我正在读入一个结构,我真的很难理解sscanf的格式。我现在正处在一个地方,我只是在尝试任何一切,所以我担心,如果我确实做对了,那将是因为我很幸运,而不是因为我实际上明白我在做什么,希望你的好人可以提供帮助。

下面是从.TXT

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

注样本数据:每个“字段”是由空格,逗号或制表符分隔。一些条目没有中间名,其他条目都遵循相同的模式。 (如果任何人有如何处理线W/O的所有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); 
    } 


} 

任何帮助,非常感谢!

+1

第一分割由逗号输入,和治疗的名称作为一个字段。然后根据空格拆分名称。 –

+1

显示的代码片段中没有输出功能。请阅读[问]并提供[mcve]。 – Olaf

+0

@Olaf输出功能添加 – user3691838

回答

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; 
} 
+0

谢谢你的帮助!没有中间名的逻辑很棒! – user3691838

+0

非常好的答案。 –

2

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

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

获得名称作为包含(可能三个)组件的字符串后,如果这些字符串存在,可以进一步细分为第一个,中间和最后一个名称。

这是一个使用这个想法的例子。请注意,代替feof(),返回值fgets()用于确定文件的所有行何时被读取。如果有多个MAX_RECORDS条目,则读取该文件的循环也可能会被终止。

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

成功扫描行输入缓冲区后,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