如何使用fscanf从忽略标点的输入文件中读取单词?

时间:2019-04-06 01:17:26

标签: c

我试图使用// Recipe class public function getAuthorAttribute() { return $this->user->username; } 从输入文件中读入,而仅读入字母并忽略特殊字符(例如逗号,句点等)。 我尝试了下面的代码,但是当我尝试打印每个输入的单词时,它不打印任何内容。

我还尝试了fscanf中的"%20[a-zA-Z]""%20[a-zA-Z] "

fscanf

1 个答案:

答案 0 :(得分:0)

目前还不清楚为什么要尝试为每个单词创建char pointer-to-pointer ,然后为每个单词分配,而只是 classify < / em> C库中[a-zA-Z]的字符在ctype.h中提供了许多像isalpha()这样的宏。

(好的,您对单词存储的评论是在回答这部分内容的同时完成的,因此我将在一分钟内添加单词处理功能)

要处理文件输入并检查每个字符是否为[a-zA-Z],您所需要做的就是打开文件并使用面向字符的输入功能(如fgetc)并使用{{1 }}。一个简短的例子就是这样:

isalpha()

(基本流I / O始终被缓冲(在Linux上为#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; } 字节),因此不会因不读入更大的缓冲区而受到惩罚)

示例输入文件

因此,如果您的输入文件混乱:

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 */ 的数量。给我一点时间,与此同时,我将为您提供帮助,摘要一下上面的基本字符分类。

分配和存储仅包含字母字符的单个单词

您可以想象,动态分配指针,然后分配和存储仅由字母字符组成的每个单词会涉及更多的内容。并没有什么困难,您只需跟踪分配的指针数量,如果您使用了所有分配的指针,请重新分配并继续操作。

新C程序员通常遇到麻烦的地方是无法 验证 ,以确保每个分配都能成功完成,从而避免写入不属于您自己的调用的内存未定义的行为

使用realloc读取单个单词是可以的。然后,为了确保您可以存储字母字符,可以将字母字符提取到单独的临时缓冲区中,并在 为该单词分配存储之前检查是否实际存储了该字符。非医学未删节词典中最长的单词为29个字符,因此大于此长度的固定缓冲区就足够了(下面使用{fscanf个字符-不要忽略缓冲区大小!

因此,存储每个单词并跟踪分配的指针数量和使用的指针数量以及要读取的固定缓冲区所需的内容类似于:

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将重新分配为当前指针数量的两倍)

inputxrealloc2函数只是辅助函数。 dupstr只需调用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);
}

内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于任何分配的内存块,您都有2个职责:(1)始终保留指向起始地址的指针因此,(2)当不再需要它时可以释放

当务之急是使用一个内存错误检查程序来确保您不会尝试访问内存或在已分配的块的边界之外/之外进行写入,不要试图以未初始化的值读取或基于条件跳转,最后,以确认您释放了已分配的所有内存。

对于Linux,$ ./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 是正常选择。每个平台都有类似的内存检查器。它们都很容易使用,只需通过它运行程序即可。

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) ,这是不必要的。请参阅:Do I cast the result of malloc?

要跳过单字符单词(或选择所需的限制),只需更改即可:

malloc

这样做会把您存储的单词更改为:

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