I\'m trying to create a code, which reads from textile, and then stores the data into memory, prints out to the screen so the user can read it, but it is still saved into the m
phrases[i].English = malloc(sizeof(char));
Here the problem lies, you are allocating a single byte and then trying to cram a string into it, which leads to undefined behavior here:
fscanf(infile,"%s", phrases[i].English);
Generally, you should assume a sensible buffer length and read that, and check whether the new line is contained. If that's not the case, read again into another buffer or enlarge the old one (using realloc). Either that or use a non-standard function like getline that does this already for you under the hood.
Given that your lines are pretty short sentences, a constant buffer size could suffice (let's call it MAX_LINE
), which provides us a little easier way to achieve the same:
fscanf(infile, "%*[^\n]s", MAX_LINE, buf);
This reads a string of length MAX_LINE
into the buffer buf
and terminates before the '\n'
is encountered.
When reading strings, you should refrain from using fscanf("%s", buf)
and use fgets() or scanf("%*s", MAX_LINE, ...)
instead. This ensures that there will be no attempt to write more data than what you specified, avoiding buffer overflows.
Edit: The nested loop shouldn't be there. You are basically overwriting phrases[i].TextSpeak
a total of phraseCounter
times for no benefit. And in the process of that you are leaking a lot of memory.
There are a number of ways to do this. However, one suggestion I would have would be to make use of line-input
tools such as fgets
or getline
to make reading the file more robust. It is fine to use fscanf
for discrete variables (I left it for reading phraseCounter
), but for reading string data of unknown length/content, line-input really should be used.
Below is an example of your code with this employed. The code is commented to explain the logic. The real logic here is the fact that you will read 2-lines for each allocated struct. A simple line counter using %
(mod) as a toggle can help you keep track of when to allocate a new struct. I also added code to accept the filename as the first argument to the program. (to run, e.g ./progname <filename>
). Let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 128
typedef struct Pair
{
char *English;
char *TextSpeak;
} Pair;
int main (int argc, char** argv) {
if (argc < 2) {
fprintf (stderr, "error: insufficient input. Usage: %s filename\n", argv[0]);
return 1;
}
Pair **pair = NULL; /* pointer to array of pointers */
char line[MAXL] = {0}; /* variable to hold line read */
FILE* infile = NULL; /* file pointer for infile */
unsigned int phraseCounter = 0; /* count of pairs */
unsigned int index = 0; /* index to pairs read */
size_t nchr = 0; /* length of line read */
size_t lnum = 0; /* line number read */
/* open file and validate */
if (!(infile = fopen ((argv[1]), "r"))) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
/* read phraseCounter */
if (!fscanf (infile, "%u%*c", &phraseCounter)) {
fprintf (stderr, "error: failed to read phraseCounter.\n");
return 1;
}
/* allocate phraseCounter number of pointers to Pair */
if (!(pair = calloc (phraseCounter, sizeof *pair))) {
fprintf (stderr, "error: memory allocation failed.\n");
return 1;
}
/* read each line in file */
while (fgets (line, MAXL - 1, infile) != NULL)
{
nchr = strlen (line); /* get the length of line */
if (nchr < 1) /* if blank or short line, skip */
continue;
if (line[nchr-1] == '\n') /* strip newline from end */
line[--nchr] = 0;
if (lnum % 2 == 0) /* even/odd test for pair index */
{
/* allocate space for pair[index] */
if (!(pair[index] = calloc (1, sizeof **pair))) {
fprintf (stderr, "error: memory allocation failed for pair[%u].\n", index);
return 1;
}
pair[index]-> English = strdup (line); /* allocate space/copy to English */
}
else
{
pair[index]-> TextSpeak = strdup (line);/* allocate space/copy to TextSpeak */
index++; /* only update index after TextSpeak read */
}
lnum++; /* increment line number */
}
if (infile) fclose (infile); /* close file pointer after read */
/* print the pairs */
printf ("\n Struct English TextSpeak\n\n");
for (nchr = 0; nchr < index; nchr++)
printf (" pair[%3zu] %-24s %s\n", nchr, pair[nchr]-> English, pair[nchr]-> TextSpeak);
/* free memory allocated to pair */
for (nchr = 0; nchr < index; nchr++)
{
if (pair[nchr]-> English) free (pair[nchr]-> English);
if (pair[nchr]-> TextSpeak) free (pair[nchr]-> TextSpeak);
if (pair[nchr]) free (pair[nchr]);
}
if (pair) free (pair);
return 0;
}
Input
$ cat dat/pairs.txt
10
nevermind
nvm
not much
nm
no problem
np
people
ppl
talk to you later
ttyl
because
cuz
i don't know
idk
as soon as possible
asap
yeah
ya
how are you
hru
Output
$ ./bin/struct_rd_pairs dat/pairs.txt
Struct English TextSpeak
pair[ 0] nevermind nvm
pair[ 1] not much nm
pair[ 2] no problem np
pair[ 3] people ppl
pair[ 4] talk to you later ttyl
pair[ 5] because cuz
pair[ 6] i don't know idk
pair[ 7] as soon as possible asap
pair[ 8] yeah ya
pair[ 9] how are you hru
Verify no memory leaks
$ valgrind ./bin/struct_rd_pairs dat/pairs.txt
==3562== Memcheck, a memory error detector
==3562== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3562== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3562== Command: ./bin/struct_rd_pairs dat/pairs.txt
==3562==
<snip>
==3562==
==3562== HEAP SUMMARY:
==3562== in use at exit: 0 bytes in 0 blocks
==3562== total heap usage: 32 allocs, 32 frees, 960 bytes allocated
==3562==
==3562== All heap blocks were freed -- no leaks are possible
==3562==
==3562== For counts of detected and suppressed errors, rerun with: -v
==3562== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)