【问题标题】:C - Get random words from text a fileC - 从文本文件中获取随机单词
【发布时间】:2017-08-30 01:30:09
【问题描述】:

我有一个文本文件,其中包含按精确顺序排列的单词列表。 我正在尝试创建一个从该文件返回单词数组的函数。我设法以与文件相同的顺序检索单词,如下所示:

char *readDict(char *fileName) {

    int i;

    char * lines[100];
    FILE *pf = fopen ("francais.txt", "r");

    if (pf == NULL) {
        printf("Unable to open the file");
    } else {

        for (i = 0; i < 100; i++) {

            lines[i] = malloc(128);

            fscanf(pf, "%s", lines[i]);

            printf("%d: %s\n", i, lines[i]);
        }


        fclose(pf);

        return *lines;
    }

    return "NULL";
}

我的问题是:如何从文本文件中返回包含随机单词的数组;不按文件字序?

文件如下所示:

exemple1
exemple2
exemple3
exemple4

【问题讨论】:

  • 好吧,以随机顺序阅读它们是没有意义的。因此,只需在阅读后随机化数组即可。见this link
  • 是的,我想过,但它让我在文件中读取了 40k 字......
  • 如果这是唯一的解决方案,我怎样才能得到文件中的字数?
  • 一边读一边数数。
  • 你为什么 return *lines; 拥有 freed 一切?

标签: c file scanf


【解决方案1】:

Reservoir sampling 允许您从不确定大小的流中选择随机数量的元素。像这样的东西可以工作(虽然未经测试):

char **reservoir_sample(const char *filename, int count) {
    FILE *file;
    char **lines;
    char buf[LINE_MAX];
    int i, n;

    file = fopen(filename, "r");
    lines = calloc(count, sizeof(char *));
    for (n = 1; fgets(buf, LINE_MAX, file); n++) {
        if (n <= count) {
            lines[n - 1] = strdup(buf);
        } else {
            i = random() % n;
            if (i < count) {
                free(lines[i]);
                lines[i] = strdup(buf);
            }
        }
    }
    fclose(file);

    return lines;
}

这是“算法 R”:

  • 将第一行 count 读入示例数组。
  • 对于每个后续行,将样本数组的随机元素替换为概率count / n,其中n 是行号。
  • 最后,样本包含一组随机行。 (顺序不是完全随机的,但您可以通过随机播放来解决此问题。)

【讨论】:

    【解决方案2】:

    如果文件的每一行包含一个单词,一种可能性是打开文件并首先计算行数。然后rewind()文件流,在文件字数范围内选择一个随机数sel。接下来,在循环中调用fgets()sel 字读入缓冲区。读取的最后一个单词可以复制到存储结果的数组中。倒带并重复所需的每个单词。

    这是一个使用 Linux 系统上典型的 /usr/share/dict/words 文件的程序。请注意,如果文件中的行数大于RAND_MAXrand() 可以返回的最大行数),则行号较大的单词将被忽略。这个数字可以小到 32767。在 GNU C 库中,RAND_MAX2147483647

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    
    #define MAX_WORD   100
    #define NUM_WORDS  10
    
    int main(void)
    {
        /* Open words file */
        FILE *fp = fopen("/usr/share/dict/words", "r");
    
        if (fp == NULL) {
            perror("Unable to locate word list");
            exit(EXIT_FAILURE);
        }
    
        /* Count words in file */
        char word[MAX_WORD];
        long wc = 0;
        while (fgets(word, sizeof word, fp) != NULL) {
            ++wc;
        }
    
        /* Store random words in array */
        char randwords[NUM_WORDS][MAX_WORD];
        srand((unsigned) time(NULL));
        for (size_t i = 0; i < NUM_WORDS; i++) {
            rewind(fp);
            int sel = rand() % wc + 1;
            for (int j = 0; j < sel; j++) {
                if (fgets(word, sizeof word, fp) == NULL) {
                    perror("Error in fgets()");
                }
            }
            strcpy(randwords[i], word);
        }
    
        if (fclose(fp) != 0) {
            perror("Unable to close file");
        }
    
        /* Display results */
        for (size_t i = 0; i < NUM_WORDS; i++) {
            printf("%s", randwords[i]);
        }
    
        return 0;
    }
    

    程序输出:

    biology's
    lists
    revamping
    slitter
    loftiness's
    concur
    solemnity's
    memories
    winch's
    boosting
    

    如果输入中的空行是一个问题,选择循环可以测试它们并在它们出现时重置以选择另一个单词:

    /* Store random words in array */
    char randwords[NUM_WORDS][MAX_WORD];
    srand((unsigned) time(NULL));
    for (size_t i = 0; i < NUM_WORDS; i++) {
        rewind(fp);
        int sel = rand() % wc + 1;
        for (int j = 0; j < sel; j++) {
            if (fgets(word, sizeof word, fp) == NULL) {
                perror("Error in fgets()");
            }
        }
        if (word[0] == '\n') {      // if line is blank
            --i;                    // reset counter
            continue;               // and select another one
        }
    
        strcpy(randwords[i], word);
    }
    

    请注意,如果文件包含个空行,则通过上述修改,程序将永远循环;计算一行中选择的空白行数并跳过直到达到某个合理的阈值可能更安全。最好在初始行数期间验证输入文件的至少一行不是空白:

    /* Count words in file */
    char word[MAX_WORD];
    long wc = 0;
    long nonblanks = 0;
    while (fgets(word, sizeof word, fp) != NULL) {
        ++wc;
        if (word[0] != '\n') {
            ++nonblanks;
        }
    }
    if (nonblanks == 0) {
        fprintf(stderr, "Input file contains only blank lines\n");
        exit(EXIT_FAILURE);
    }
    

    【讨论】:

    • 看起来不错,但它计算文件中的空白行吗?谢谢
    • @greenpoisononeTV——是的,可以选择空行。它们需要被计算在内,因为这种方法通过知道文件中的行数来工作,但不需要选择它们。您可以在选择循环中添加三行来完成此操作。我已经更新了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 2014-06-25
    • 1970-01-01
    相关资源
    最近更新 更多