I'm trying to upgrade my Hangman game by reading random words from a .txt file. Thing is, I can't figure out how to read a random line from the .txt file. There are single words on every new line of the .txt file.
void ler_palavras()
{
FILE *words;
if ((words = fopen("words.txt", "r")) == NULL) {
printf("Error! opening file");
exit(1);
}
// reads text until newline
fscanf(words,"%[^\n]", word);
fclose(words);
}
Here is another solution, still limited by
RAND_MAX
, that doesn't require reading each line up to the chosen line. The idea is to use a binary file that stores each word in the same number of bytes, so any word can be accessed by usingfseek()
andfread()
. The first entry in the file is along
value that stores the number of words in the file. When words are added, this value is updated.Here is an implementation that looks for an ordinary text file called
wordlist.txt
, which has one word on each line. If found, the program updates (or creates, if necessary) a file calledwordlist.fmt
. The update function reads in each word from the text file, skipping blank lines, and stores it in the binary file in a fixed number of bytes. After reading in all of the words, the word count is updated. After running the program once with a text file, you should remove the text file, or the next run will add the words again. The.fmt
file should stay, and if you want to add more words, just put a new text file in the directory with the executable and run it again.The loop that prints five random words generates a random number, uses that number to move to a file position containing a word, reads that word into an array, and prints it.
rand()
has its limitations including only generating values0
toRAND_MAX
and a file can have many timesRAND_MAX
lines. Assuming the line count is on the order ofRAND_MAX/10
or less, the following will meet OP's goals.Perform one pass to count the number of lines. -->
lc
For each random line needed, re-read the file's lines, from beginning up the line index before some random number in the
[0... lc-1]
range.Then simply read and print that line. No need for a line buffer. The file is the line buffer. The code re-uses
Line_Count()
for both the total line count calculation and for reading until the nth line.If, for some reason, you can't just load the whole set of lines into memory (too big or whatever), there is a way to select a random entry from a streaming set of entries. It won't scale indefinitely, and it will exhibit small biases, but this is a game, not cryptography, so that shouldn't be a dealbreaker.
The logic is:
double
(e.g. withdrand48
or whatever PRNG facilities are available to you)1.0 / lineno > randval
, replace the currently stored word with the word from the current line (so the first line is auto stored, the second line is 50% likely to replace it, the third is 33% likely to do so, etc.)word
is your selectionAssuming the number of lines is small enough (and the range of
double
s produced by your PRNG is fine-grained enough), this gives as close as possible to an equal likelihood of any given line being selected; for two lines, each has a 50/50 shot, for three, 33.33...%, etc.I lack a C compiler right now, but the basic code would look like: