0

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 memory so you can use it for the rest of the program..

Here is the sample of the textile

            75
            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
            you

the list goes on, it has a total of 150 words, 151 lines if the first number is included. The 75 serves to tell you how many pairs there are.

anyways, here is the code that i Have written so far, it uses this struct

            typedef struct
            {
                char *English;
                char *TextSpeak;
            }Pair;

The code i have written so far is:

                FILE *infile =fopen("dictionary.txt","r");

                int phraseCounter;
                fscanf(infile, "%i", &phraseCounter); //Now you have the number of phrase pairs

                //Allocate memory
                Pair *phrases=malloc(sizeof(Pair) * phraseCounter);

                //Run loop
                for(int i=0; i < phraseCounter; i++){
                    //Get the english word
                    phrases[i].English = malloc(sizeof(char));
                    fscanf(infile,"%s",phrases[i].English);

                    //run loop to read next line
                    for(int a=0; a < phraseCounter; a++){
                        phrases[i].TextSpeak = malloc(sizeof(char));
                        fscanf(infile,"%s",phrases[i].TextSpeak);
                    }
                    printf("%s - %s\n", phrases[i].English, phrases[i].TextSpeak);

                }

                fclose(infile);

                for(int i=0; i < phraseCounter; i++)
                    free(phrases[i].English);


                free(phrases);

The output i keep getting is:

            nevermind - atm
            by - definitely
            def - 
            - 
            - 
            - 
            - 
            - 

And it keeps going for 75 lines.

Now I'm not sure whether I should use a 2D array or if this will be acceptable. Any help will be appreciated! Thanks

2 Answers2

0
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.

a3f
  • 8,517
  • 1
  • 41
  • 46
0

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)
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85