C - 从文本中获取随机单词

时间:2017-04-04 17:45:27

标签: c file scanf

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

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

2 个答案:

答案 0 :(得分:2)

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是行号。
  • 最后,样本包含一组随机行。 (顺序不是一成不变的,但你可以通过随机播放来解决这个问题。)

答案 1 :(得分:1)

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

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

#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);
}