如何阅读两个文本文件并计算关键字的数量?

时间:2015-04-03 19:13:04

标签: c arrays string file io

我试过环顾四周,但对我而言,文件是最难理解的,因为我正在学习C,尤其是文本文件,二进制文件更容易一些。基本上我必须阅读两个文本文件,其中包含格式如下的单词"硬,工作,智能,运行良好等等。"我想比较文本文件并计算关键字。我会展示一些代码,但说实话,我迷失了,而且我唯一能做的就是胡说八道。

#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define SIZE 1000

void resumeRater();
int main()
{

    int i;
    int counter = 0;

    char array[SIZE];
    char keyword[SIZE];

    FILE *fp1, *fp2;
    int ch1, ch2;

    errno_t result1 = fopen_s(&fp1, "c:\\myFiles\\resume.txt", "r");
    errno_t result2 = fopen_s(&fp2, "c:\\myFiles\\ideal.txt", "r");
    if (fp1 == NULL) {
        printf("Failed to open");
    }
    else if (fp2 == NULL) {
        printf("Failed to open");
    }
    else {
        result1 = fread(array, sizeof(char), 1, fp1);
        result2 = fread(keyword, sizeof(char), 1, fp2);
        for (i = 0; i < SIZE; i++)
        {
            if (array[i] == keyword[i])
            {
                counter++;
            }
        }

        fclose(fp1);
        fclose(fp2);
        printf("Character match: %d", counter);
    }
    system("pause");
}

1 个答案:

答案 0 :(得分:0)

当你遇到多重事物(比如阅读2个文件)的情况时,提前计划是很有意义的。而不是使用读取2个文本文件所需的所有代码来混淆main的主体,而是创建一个函数来为您读取文本文件并让它返回包含文件行的数组。这真的可以帮助您专注于代码需要对行进行操作的逻辑,而不是通过首先获取行来填充空间。现在,在一个长main中填写所有内容并没有错,但从可读性,维护和程序结构的角度来看,这会让事情变得更加困难。

如果您很好地构建了读取功能,则可以将main缩减为以下内容。这会将文本文件读入字符数组,并提供总共4行读取的行数(加上检查以确保提供的两个文件名可读):

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

    if (argc < 3 ) {
        fprintf (stderr, "error: insufficient input, usage: %s <filename1> <filename2>\n", argv[0]);
        return 1;
    }

    size_t file1_size = 0;  /* placeholders to be filled by readtxtfile */
    size_t file2_size = 0;  /* for general use, not needed to iterate   */

    /* read each file into an array of strings, 
    number of lines read, returned in file_size */
    char **file1 = readtxtfile (argv[1], &file1_size);
    char **file2 = readtxtfile (argv[2], &file2_size);

    return 0;
}

此时您拥有所有数据,并且可以处理关键字代码。从文本文件中读取是一件非常简单的事情。您只需要熟悉可用的工具。在阅读文本的时,首选方法是使用行输入一次将整行读入缓冲区。然后解析缓冲区以获得所需内容。行输入工具是fgetsgetline。阅读完专线后,您就可以使用strtokstrsepsscanf等工具来分隔您想要的内容。 fgetsgetline都会在每行末尾读取newline作为其输入的一部分,因此您可能需要删除newline以满足您的需求。

存储每行读取通常是通过声明指向char *指针数组的指针来完成的。 (例如char **file1;)然后为一些初始指针分配内存。 (以下示例中的NMAX)然后,当file1_array[n]是文件的行索引n时,您将0 - lastline访问文件中的各行。如果您有一个大文件并超过最初分配的指针数,则只需使用realloc为您的阵列重新分配其他指针。 (您可以将NMAX设置为1以使每个行都能发生这种情况)

用于分配内存以及重新分配的方式会影响您在程序中使用数组的方式。仔细选择calloc以初始分配数组,然后在重新分配以将所有未使用的指针设置为memset(null)时使用0,真的可以节省您的时间和头痛吗?为什么?因为要迭代你的数组,你需要做的就是:

n = 0; 
while (file1[n]) {
    <do something with file1[n]>; 
    n++;
}

当您到达第一个未使用的指针(即file1[n]的第一个0)时,循环停止。

阅读文本文件时另一个非常有用的功能是strdup (char *line)strdup将使用line自动为malloc分配空间,将line复制到新分配的内存,并返回指向新内存块的指针。这意味着您需要做的就是为每个指针分配空间并将getline准备好的行复制到您的数组:

file1[n] = strdup (line);

几乎就是这样。您已经读取了文件并填充了数组,并知道如何遍历数组中的每一行。剩下的就是清理并释放不再需要时分配的内存。通过确保未使用的指针为0,这也很容易。您只需再次迭代file1[n]指针,随意释放它们,最后再free (file1)。你做完了。

这是需要考虑的事情,还有一些事情要做。在初始读取文件时,如果您注意到,我们还声明了一个file1_size = 0;变量,并将其地址传递给read函数:

char **file1 = readtxtfile (argv[1], &file1_size);

readtxtfile内,每次读取一行时,file1_size地址的值都会增加1。当readtxtfile返回时,file1_size包含读取的行数。如图所示,迭代file1数组不需要这样做,但是您经常需要知道已经阅读了多少行。

为了把这一切放在一起,我创建了一个简短的函数示例来读取两个文本文件,打印两行中的行并释放与文件数组相关的内存。这个解释比我预期的要长。因此,花点时间了解它是如何工作的,您将更容易接近处理文本文件。下面的代码将2个文件名作为参数(例如./progname file1 file2)使用与gcc -Wall -Wextra -o progname srcfilename.c类似的内容进行编译:

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

#define NMAX 256

char **readtxtfile (char *fn, size_t *idx);
char **realloc_char (char **p, size_t *n);
void prn_chararray (char **ca);
void free_chararray (char **ca);

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

    if (argc < 3 ) {
        fprintf (stderr, "error: insufficient input, usage: %s <filename1> <filename2>\n", argv[0]);
        return 1;
    }

    size_t file1_size = 0;  /* placeholders to be filled by readtxtfile */
    size_t file2_size = 0;  /* for general use, not needed to iterate   */

    /* read each file into an array of strings, 
    number of lines read, returned in file_size */
    char **file1 = readtxtfile (argv[1], &file1_size);
    char **file2 = readtxtfile (argv[2], &file2_size);

    /* simple print function */
    if (file1) prn_chararray (file1);
    if (file2) prn_chararray (file2);

    /* simple free memory function */
    if (file1) free_chararray (file1);
    if (file2) free_chararray (file2);

    return 0;
}

char** readtxtfile (char *fn, size_t *idx)
{
    if (!fn) return NULL;           /* validate filename provided       */

    char *ln = NULL;                /* NULL forces getline to allocate  */
    size_t n = 0;                   /* max chars to read (0 - no limit) */
    ssize_t nchr = 0;               /* number of chars actually read    */
    size_t nmax = NMAX;             /* check for reallocation           */
    char **array = NULL;            /* array to hold lines read         */
    FILE *fp = NULL;                /* file pointer to open file fn     */

    /* open / validate file */
    if (!(fp = fopen (fn, "r"))) {
        fprintf (stderr, "%s() error: file open failed '%s'.", __func__, fn);
        return NULL;
    }

    /* allocate NMAX pointers to char* */
    if (!(array = calloc (NMAX, sizeof *array))) {
        fprintf (stderr, "%s() error: memory allocation failed.", __func__);
        return NULL;
    }

    /* read each line from fp - dynamicallly allocated   */
    while ((nchr = getline (&ln, &n, fp)) != -1)
    {
        /* strip newline or carriage rtn    */
        while (nchr > 0 && (ln[nchr-1] == '\n' || ln[nchr-1] == '\r'))
            ln[--nchr] = 0;

        array[*idx] = strdup (ln);  /* allocate/copy ln to array        */

        (*idx)++;                   /* increment value at index         */

        if (*idx == nmax)           /* if lines exceed nmax, reallocate */
            array = realloc_char (array, &nmax);
    }

    if (ln) free (ln);              /* free memory allocated by getline */
    if (fp) fclose (fp);            /* close open file descriptor       */

    return array;
}

/* print an array of character pointers. */
void prn_chararray (char **ca)
{
    register size_t n = 0;
    while (ca[n])
    {
        printf (" arr[%3zu]  %s\n", n, ca[n]);
        n++;
    }
}

/* free array of char* */
void free_chararray (char **ca)
{
    if (!ca) return;
    register size_t n = 0;
    while (ca[n])
        free (ca[n++]);
    free (ca);
}

/*  realloc an array of pointers to strings setting memory to 0.
*  reallocate an array of character arrays setting
*  newly allocated memory to 0 to allow iteration
*/
char **realloc_char (char **p, size_t *n)
{
    char **tmp = realloc (p, 2 * *n * sizeof *p);
    if (!tmp) {
        fprintf (stderr, "%s() error: reallocation failure.\n", __func__);
        // return NULL;
        exit (EXIT_FAILURE);
    }
    p = tmp;
    memset (p + *n, 0, *n * sizeof *p); /* memset new ptrs 0 */
    *n *= 2;

    return p;
}

valgrind - 不要忘记检查泄漏

最后,无论何时在代码中分配内存,请确保使用内存检查程序(如valgrind)确认没有内存错误并确认没有内存泄漏(即已忘记分配的内存块)免费的,或者已经无法访问的)。 valgrind简单易用,只需valgrind ./progname [any arguments]。它可以提供丰富的信息。例如,在这个阅读示例中:

$ valgrind ./bin/getline_readfile_fn voidstruct.c wii-u.txt
==14690== Memcheck, a memory error detector
==14690== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==14690== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==14690== Command: ./bin/getline_readfile_fn voidstruct.c wii-u.txt
==14690==
        <snip - program output>
==14690==
==14690== HEAP SUMMARY:
==14690==     in use at exit: 0 bytes in 0 blocks
==14690==   total heap usage: 61 allocs, 61 frees, 6,450 bytes allocated
==14690==
==14690== All heap blocks were freed -- no leaks are possible
==14690==
==14690== For counts of detected and suppressed errors, rerun with: -v
==14690== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

特别注意这些界限:

==14690== All heap blocks were freed -- no leaks are possible

==14690== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

您可以忽略仅显示我没有为libc安装开发文件的(suppressed: 2 from 2)