簡體   English   中英

如何使用fscanf從忽略標點的輸入文件中讀取單詞?

[英]How to read in words from an input file that ignores punctuation using fscanf?

我試圖使用fscanf從輸入文件中讀入,而僅讀入字母並忽略特殊字符(例如逗號,句點等)。我嘗試了下面的代碼,但是當我嘗試打印每個輸入單詞時它不打印任何內容。

我還在fscanf嘗試了"%20[a-zA-Z]""%20[a-zA-Z] "

char** input;
input = (char **)malloc(numWordsInput*sizeof(char*));

for (i = 0; i < numWordsInput; i++)
{
  fscanf(in_file, "%s", buffer);
  sLength = strlen(buffer)+1;
  input[i] = (char *)malloc(sLength*sizeof(char));
}
rewind(in_file);
for (i = 0; i < numWordsInput; i++)
{
  fscanf(in_file, "%20[a-zA-Z]%*[a-zA-Z]", input[i]);
}

目前尚不清楚為什么您要嘗試為每個單詞創建一個指向 char 的指針 ,然后為每個單詞分配,但是為了簡單地 [a-zA-Z]字符進行分類 ,C庫提供了一個數字像isalpha()這樣的ctype.h的宏可以做到這一點。

(好的,您對單詞存儲的評論是在答案的這一部分完成后得出的,因此,我將在一分鍾內添加單詞處理功能)

要處理文件輸入並檢查每個字符是否為[a-zA-Z] ,您要做的就是打開文件並使用面向字符的輸入函數(如fgetc並使用isalpha()測試每個字符。 一個簡短的例子就是:

#include <stdio.h>
#include <ctype.h>

int main (int argc, char **argv) {

    int c;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while ((c = fgetc (fp)) != EOF)     /* read each char in file */
        if (isalpha (c) || c == '\n')   /* is it a-zA-Z or \n  */
            putchar (c);                /* output it */

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

(無論如何,基本流I / O都已緩沖(在Linux上為8192字節),因此不會因不讀入更大的緩沖區而受到懲罰)

輸入文件示例

因此,如果您的輸入文件混亂:

$ cat ../dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a

- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495

使用/輸出示例

...並只想從中選擇[a-zA-Z]字符(和'\\n'字符以保留示例的行距),您將得到:

$ ./bin/readalpha ../dat/10intmess.txt
aa
aa

TheQuick
BrownFox
JumpsOver
A
Lazya
Dog

如果您還想包含[0-9] ,則只需使用isalnum (c)而不是isalpha (c)

您也可以一次閱讀一行(或一次閱讀一個單詞),而只需在指針上向下滑動指針即可完成相同的操作。 例如,您可以執行以下操作:

#include <stdio.h>
#include <ctype.h>

#define MAXC 4096

int main (int argc, char **argv) {

    char buf[MAXC];
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {             /* read each line in file */
        char *p = buf;                          /* pointer to bufffer */
        while (*p) {                            /* loop over each char */
            if (isalpha (*p) || *p == '\n')     /* is it a-zA-Z or \n  */
                putchar (*p);                   /* output it */
            p++;
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

(輸出是相同的)

或者,如果您更喜歡使用索引而不是指針,則可以使用:

    while (fgets (buf, MAXC, fp))               /* read each line in file */
        for (int i = 0; buf[i]; i++)            /* loop over each char */
            if (isalpha (buf[i]) || buf[i] == '\n') /* is it a-zA-Z or \n  */
                putchar (buf[i]);               /* output it */

(輸出是相同的)

仔細檢查一下,如果您有任何問題,請告訴我。 如果你需要做一個字的時間,你將有顯著添加到您的代碼,以保護您的指針數量和realloc要求。 給我一點時間,與此同時,我將為您提供幫助,以幫助您總結上面的基本字符分類。

分配和存儲僅包含字母字符的單詞

正如您可以想象的那樣,要動態地分配指針,然后分配並存儲僅由字母字符組成的每個單詞,這要復雜得多。 這並沒有什么困難,您只需跟蹤分配的指針數量,如果您使用了所有分配的指針,請重新分配並繼續前進。

新C程序員經常遇到麻煩的地方是無法驗證每個必要步驟以確保每個分配成功,從而避免寫入不由您自己調用的未定義行為的內存。

fscanf讀取單個單詞很好。 然后,為確保您可以存儲字母字符, 為該單詞分配存儲空間之前 ,有必要將字母字符提取到單獨的臨時緩沖區中並檢查是否實際存儲了任何字符。 非醫學未刪節詞典中的最長單詞為29個字符,因此大於此長度的固定緩沖區就足夠了(下面使用1024字符- 不要跳過緩沖區大小!

因此,存儲每個單詞並跟蹤分配的指針數量和使用的指針數量以及要讀取的固定緩沖區所需的內容類似於:

#define NPTR    8   /* initial number of pointers */
#define MAXC 1024
...
    char **input,           /* pointers to words */
        buf[MAXC];          /* read buffer */
    size_t  nptr = NPTR,    /* number of allcoated pointers */
            used = 0;       /* number of used pointers */

分配完初始數量的指針后,您可以讀取每個單詞,然后從中解析出字母字符,類似於以下內容:

    while (fscanf (fp, "%s", buf) == 1) {       /* read each word in file  */
        size_t ndx = 0;                         /* alpha char index */
        char tmp[MAXC];                         /* temp buffer for alpha */
        if (used == nptr)                       /* check if realloc needed */
            input = xrealloc2 (input, sizeof *input, &nptr);    /* realloc */
        for (int i = 0; buf[i]; i++)            /* loop over each char */
            if (isalpha (buf[i]))               /* is it a-zA-Z or \n  */
                tmp[ndx++] = buf[i];            /* store alpha chars */
        if (!ndx)                               /* if no alpha-chars */
            continue;                           /* get next word */
        tmp[ndx] = 0;                           /* nul-terminate chars */
        input[used] = dupstr (tmp);             /* allocate/copy tmp */
        if (!input[used]) {                     /* validate word storage */
            if (used)           /* if words already stored */
                break;          /* break, earlier words still good */
            else {              /* otherwise bail */
                fputs ("error: allocating 1st word.\n", stderr);
                return 1;
            }
        }
        used++;                                 /* increment used count */
    }

注意:used指針數量等於分配的數量時, input將重新分配為當前指針數量的兩倍)

xrealloc2dupstr函數只是輔助函數。 xrealloc2只需調用realloc並加倍當前分配的大小,驗證分配並在成功時返回已分配的指針,或者在失敗時返回當前指針-您可以將其更改為返回NULL來處理錯誤(如果您願意)。

/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
 *  returns pointer to reallocated block of memory with new
 *  memory initialized to 0/NULL. return must be assigned to
 *  original pointer in caller.
 */
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem)
{   void *memptr = realloc ((char *)ptr, *nelem * 2 * psz);
    if (!memptr) {
        perror ("realloc(): virtual memory exhausted.");
        exit (EXIT_FAILURE);
        /* return NULL; */
    }   /* zero new memory (optional) */
    memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
    *nelem *= 2;
    return memptr;
}

dupstr函數只是一個普通的strdup ,但由於並非所有編譯器都提供strdup ,因此它用於確保可移植性。

/** allocate storage for s + 1 chars and copy contents of s
 *  to allocated block returning new sting on success,
 *  NULL otherwise.
 */
char *dupstr (const char *s)
{
    size_t len = strlen (s);
    char *str = malloc (len + 1);

    if (!str)
        return NULL;

    return memcpy (str, s, len + 1);
}

使用助手只會使代碼的主體保持一點清潔度,而不是將其全部塞入循環中。

完全可以做到:

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

#define NPTR    8   /* initial number of pointers */
#define MAXC 1024

void *xrealloc2 (void *ptr, size_t psz, size_t *nelem);
char *dupstr (const char *s);

int main (int argc, char **argv) {

    char **input,           /* pointers to words */
        buf[MAXC];          /* read buffer */
    size_t  nptr = NPTR,    /* number of allcoated pointers */
            used = 0;       /* number of used pointers */
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    input = malloc (nptr * sizeof *input);  /* allocate nptr pointers */
    if (!input) {                           /* validate every allocation */
        perror ("malloc-input");
        return 1;
    }

    while (fscanf (fp, "%s", buf) == 1) {       /* read each word in file  */
        size_t ndx = 0;                         /* alpha char index */
        char tmp[MAXC];                         /* temp buffer for alpha */
        if (used == nptr)                       /* check if realloc needed */
            input = xrealloc2 (input, sizeof *input, &nptr);    /* realloc */
        for (int i = 0; buf[i]; i++)            /* loop over each char */
            if (isalpha (buf[i]))               /* is it a-zA-Z or \n  */
                tmp[ndx++] = buf[i];            /* store alpha chars */
        if (!ndx)                               /* if no alpha-chars */
            continue;                           /* get next word */
        tmp[ndx] = 0;                           /* nul-terminate chars */
        input[used] = dupstr (tmp);             /* allocate/copy tmp */
        if (!input[used]) {                     /* validate word storage */
            if (used)           /* if words already stored */
                break;          /* break, earlier words still good */
            else {              /* otherwise bail */
                fputs ("error: allocating 1st word.\n", stderr);
                return 1;
            }
        }
        used++;                                 /* increment used count */
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    for (size_t i = 0; i < used; i++) {
        printf ("word[%3zu]: %s\n", i, input[i]);
        free (input[i]);    /* free storage when done with word */
    }
    free (input);           /* free pointers */

    return 0;
}

/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
 *  returns pointer to reallocated block of memory with new
 *  memory initialized to 0/NULL. return must be assigned to
 *  original pointer in caller.
 */
void *xrealloc2 (void *ptr, size_t psz, size_t *nelem)
{   void *memptr = realloc ((char *)ptr, *nelem * 2 * psz);
    if (!memptr) {
        perror ("realloc(): virtual memory exhausted.");
        exit (EXIT_FAILURE);
        /* return NULL; */
    }   /* zero new memory (optional) */
    memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
    *nelem *= 2;
    return memptr;
}

/** allocate storage for s + 1 chars and copy contents of s
 *  to allocated block returning new sting on success,
 *  NULL otherwise.
 */
char *dupstr (const char *s)
{
    size_t len = strlen (s);
    char *str = malloc (len + 1);

    if (!str)
        return NULL;

    return memcpy (str, s, len + 1);
}

(使用相同的輸入文件)

使用/輸出示例

$ ./bin/readalphadyn ../dat/10intmess.txt
word[  0]: a
word[  1]: a
word[  2]: a
word[  3]: a
word[  4]: The
word[  5]: Quick
word[  6]: Brown
word[  7]: Fox
word[  8]: Jumps
word[  9]: Over
word[ 10]: A
word[ 11]: Lazy
word[ 12]: a
word[ 13]: Dog

內存使用/錯誤檢查

在您編寫的任何可以動態分配內存的代碼中,對於任何分配的內存塊,您都有2個責任 :(1) 始終保留指向該內存塊起始地址的指針,因此,(2)在沒有內存塊時可以將其釋放需要更長的時間。

必須使用一個內存錯誤檢查程序來確保您不嘗試訪問內存或不要在已分配的塊的邊界之外/之外進行寫入,不要嘗試以未初始化的值讀取或基於條件跳轉,最后確定您可以釋放已分配的所有內存。

對於Linux, valgrind是通常的選擇。 每個平台都有類似的內存檢查器。 它們都很容易使用,只需通過它運行程序即可。

$ valgrind ./bin/readalphadyn ../dat/10intmess.txt
==8765== Memcheck, a memory error detector
==8765== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8765== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==8765== Command: ./bin/readalphadyn ../dat/10intmess.txt
==8765==
word[  0]: a
word[  1]: a
word[  2]: a
word[  3]: a
word[  4]: The
word[  5]: Quick
word[  6]: Brown
word[  7]: Fox
word[  8]: Jumps
word[  9]: Over
word[ 10]: A
word[ 11]: Lazy
word[ 12]: a
word[ 13]: Dog
==8765==
==8765== HEAP SUMMARY:
==8765==     in use at exit: 0 bytes in 0 blocks
==8765==   total heap usage: 17 allocs, 17 frees, 796 bytes allocated
==8765==
==8765== All heap blocks were freed -- no leaks are possible
==8765==
==8765== For counts of detected and suppressed errors, rerun with: -v
==8765== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始終確認已釋放已分配的所有內存,並且沒有內存錯誤。

注意:無需malloc的返回值,這是不必要的。請參閱: 是否強制轉換malloc的結果?

要跳過單字符單詞(或選擇所需的限制),只需更改即可:

    if (ndx < 2)                            /* if 0/1 alpha-chars */
        continue;                           /* get next word */

這樣做會將您存儲的單詞更改為:

$ ./bin/readalphadyn ../dat/10intmess.txt
word[  0]: The
word[  1]: Quick
word[  2]: Brown
word[  3]: Fox
word[  4]: Jumps
word[  5]: Over
word[  6]: Lazy
word[  7]: Dog

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM