2017-07-30 79 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