0

I am currently working on a a program whose goal is to take words from a file, and store them into an array, while outputting if they are at the beginning of a row, or a word on the line. The file looks like this: at the end of each line there is a final space, and a line break.

abbe able 
abet abut 
able abbe ably axle 
ably abbe able ally 
abut abbe abet 
aced aces acid aged aped awed axed iced 
aces aced acts ages ales apes axes ices 
ache achy acme acne acre 
achy ache ashy 

The code that I have currently works in the terminal, outputting:

Row: abbe able,
Row: abet abut,
Row: able abbe, ably, axle,
Row: ably abbe, able, ally,
Row: abut abbe, abet,
Row: aced aces, acid, aged, aped, awed, axed, iced,
Row: aces aced, acts, ages, ales, apes, axes, ices,
Row: ache achy, acme, acne, acre,
Row: achy ache, ashy,

However, when I run it with valgrind, I run into an issue, it doesn't recognize rows, instead outputting only the first 2 letters as each row.

Row: abbe able,
ab, abut,
ab, abbe, ably, axle,
ab, abbe, able, ally,
ab, abbe, abet,
ac, aces, acid, aged, aped, awed, axed, iced,
ac, aced, acts, ages, ales, apes, axes, ices,
ac, achy, acme, acne, acre,
ac, ache, ashy,

After it finishes outputting all of the characters, it reads this:

*** stack smashing detected ***: terminated
==24==
==24== Process terminating with default action of signal 6 (SIGABRT)
==24==    at 0x489818B: raise (raise.c:51)
==24==    by 0x4877858: abort (abort.c:79)
==24==    by 0x48E23ED: __libc_message (libc_fatal.c:155)
==24==    by 0x49849B9: __fortify_fail (fortify_fail.c:26)
==24==    by 0x4984985: __stack_chk_fail (stack_chk_fail.c:24)
==24==    by 0x1094B0: main (in /mnt/c/Users/Jordan/Documents/GitHub/flwg/test)
zo, boos, coos, moos, woos, zoom,==24==
==24== HEAP SUMMARY:
==24==     in use at exit: 0 bytes in 0 blocks
==24==   total heap usage: 19,833 allocs, 19,833 frees, 266,441 bytes allocated
==24==
==24== All heap blocks were freed -- no leaks are possible
==24==
==24== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Aborted (core dumped)

Here is the code. Any help would be greatly appreciated!!

int main(){
        
    /* The Amount of words in each file, File 1, 2, 3 */
    int totalWordQuantity = 19829; 
    /*the word that we test, we add by two because first 4: word, 5th: \n, 6th: \0*/
    char word[5]; 
    /*how many times we've changed the character*/
    int letterSpot = 0; 
    /*Four Letter Word Document*/
    FILE *flwd = fopen("WordDocuments/Four_Letter_Connections.txt", "r"); 
    if(flwd == NULL){
        printf("File Failed");
        
    }
    /* is it the first word of the line */
    int wordCount = 0; 
    /* P is a step ahead of c, because otherwise the words get super messed up. */
    int p = fgetc(flwd); 
    /*the character that goes through the file*/
    int c = p; 
    /* So, this is a temporary word, who will store the row, so all of its category folks will be contained within it */
    /*This stores all of the words*/
    char** wordStorage = (char**)calloc(totalWordQuantity, sizeof(char*));
    int i; 
    for(i = 0; i < totalWordQuantity; i++){
        wordStorage[i] = malloc(sizeof(char) * 5);  
    }
    int nextIsRow = 1;  
    /* First, take the character */
    while(wordCount < totalWordQuantity){
        
        p = fgetc(flwd);

        /* Add the character to the word */
        word[letterSpot] = (char)c; 
 
        /* Allows the letter spot to find the next place into the word */
        letterSpot++; 
         
        if(c == ' '){
            letterSpot = 0;
             
            word[4] = '\0'; 
        
            
            strcpy(wordStorage[wordCount], word);  
            if(nextIsRow == 1){
                printf("\nRow: %s", wordStorage[wordCount]); 
                
            
            }
            else{
                printf(" %s,", wordStorage[wordCount]); 
                              
            }
            
            if(p == '\n'){
                nextIsRow = 1;  
                p = fgetc(flwd); 
                    
            }
            else{
                nextIsRow = 0; 
            }
            wordCount++;
        }
        c=p;
    } 
    
    fclose(flwd);
     
    //Frees up word storage
    for(i = 0; i < totalWordQuantity; i++){
        free(wordStorage[i]); 
    
    }
    free(wordStorage);


    return 0;

Thank you for taking a look!

Some additional details: if I output something under the if(p == '\n') line, it will not output in valgrind, however, it will in the terminal. Also, I've verified that it only puts words in allocated locations.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 2
    If you are simply outputting what you have read in the format shown, why you are allocating and storing anything to begin with? It would seem you could simply use a single character array to buffer 4-character and keep a counter of which group of 4 is being read to output what you show you want? Or, you could declare a buffer to hold the entire like, make one call to `fgets()` and then use `strtok` (or similar) to tokenize the line outputting the individual words. Is there some other reason you want to store things? – David C. Rankin Aug 08 '20 at 04:18
  • 2
    Your logic is hard to follow, but you're probably overflowing the `word` array. print `letterspot` before `word[letterSpot] = (char)c;` and make sure it's never more than 4. – Barmar Aug 08 '20 at 04:21
  • @Barmar Alright! Thank you for the feedback, I printed out letterSpot in and it never went above 4. – Jordan Driscoll Aug 08 '20 at 06:13
  • @DavidC.Rankin Thank you for the advice! I'm actually going to try redoing it, this time using fgets, from the looks of it, it seems like it will make it a lot easier! I'll get back once I get the results – Jordan Driscoll Aug 08 '20 at 08:47
  • OT regarding: `char** wordStorage = (char**)calloc(totalWordQuantity, sizeof(char*));` 1) In C, the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code and is error prone. Suggest removing that cast. 2) the function: `calloc()` can fail. so always check (!=NULL) the returned value to assure the operation was successful. If not successful (==NULL) then call `perror( "calloc failed" ); then clean up, then call: `exit( EXIT_FAILURE );` – user3629249 Aug 08 '20 at 16:50
  • regarding: `/*the word that we test, we add by two because first 4: word, 5th: \n, 6th: \0*/ char word[5]; ` this only allocates 5 characters, but the comment says it needs 6 characters. Suggest: `char word[6];` – user3629249 Aug 08 '20 at 17:00
  • @DavidC.Rankin I took your advice and it actually worked flawlessly! Thank you so much for your time, recoding it using the the functions you suggested made it work marvelously, I've been working on this problem for ages and I can't thank you enough!! Thank you everyone who contributed and took a look at my code, as mentioned all help is greatly appreciated!! – Jordan Driscoll Aug 09 '20 at 04:13
  • @JordanDriscoll - well done for pushing though and solving the problem. There are at least three ways I can think of to effectively handle this read and output. Two of which were in my comment. Just goes to show there is always more than one way to *skin-the-cat* with C. Good luck with your coding! – David C. Rankin Aug 09 '20 at 05:25
  • regarding this code block: `FILE *flwd = fopen("WordDocuments/Four_Letter_Connections.txt", "r"); if(flwd == NULL) { printf("File Failed"); }` since the code cannot access the input file, must not continue executing code. Therefore, need to insert after the displaying of the error message: `exit( EXIT_FAILURE );` so the code exits – user3629249 Aug 09 '20 at 17:03
  • regarding this kind of output: `Row: achy ache, ashy,` This places a comma `,` after the end of each row and fails to insert a comma `,` between the first and second word of each row. It that actually what you want? – user3629249 Aug 09 '20 at 17:16

2 Answers2

3

The following proposed code:

  1. cleanly compiles
  2. performs the desired functionality
  3. properly passes error messages to stderr
  4. places a comma + space after every word
  5. uses a '#define' to avoid 'magic' numbers in code

and now, the proposed code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE_LEN 1024

int main( void )
{
    /*Four Letter Word Document*/
    FILE *flwd = fopen( "WordDocuments/Four_Letter_Connections.txt", "r" ); 
    if( flwd == NULL )
    {
        perror( "fopen Failed" ); 
        exit( EXIT_FAILURE );
    }
    
    size_t rowCount = 0;
    char lineBuf[ MAX_LINE_LEN ];
    
    while( fgets( lineBuf, MAX_LINE_LEN, flwd ) )
    {
        // remove trailing '\n'
        lineBuf[ strcspn( lineBuf, "\n" ) ] = '\0';

        rowCount++;
        printf( "Row: %4zu ", rowCount );
        
        char *token = strtok( lineBuf, " " );
        while( token )
        {
            printf( "%s, ", token );
            token = strtok( NULL, " " );
        }
        printf( "\n" );
    } 

    fclose( flwd );
    return 0;
}

a run of the code, using the OPs listed input file results in:

Row:    1 abbe, able, 
Row:    2 abet, abut, 
Row:    3 able, abbe, ably, axle, 
Row:    4 ably, abbe, able, ally, 
Row:    5 abut, abbe, abet, 
Row:    6 aced, aces, acid, aged, aped, awed, axed, iced, 
Row:    7 aces, aced, acts, ages, ales, apes, axes, ices, 
Row:    8 ache, achy, acme, acne, acre, 
Row:    9 achy, ache, ashy, 
user3629249
  • 16,402
  • 1
  • 16
  • 17
1

Well, your code is a bit messy. And, its been hard to read. So, I would suggest try to clean your code and do things, those are only necessary. I'm providing a piece of code that does the same as your code. Take a look, and try to make your code better, and may be in the process it would be clear to you what is wrong:

Update:

Thanks to @AnttiHaapala, as (s)he pointed out. I was using char ch = fgetc(), while, fgetc() returns int, which causes problems with specific compilers(which declares char as unsigned).

Here's the updated code.

#include <stdio.h>
#include <stdlib.h>

#define BUF_SIZE 1024

int readLine(FILE *fp, char *buf) {
    int i = 0;

    int ch = fgetc(fp); // as fgetc() returns int
    while(ch != '\n' && ch != EOF) {    
        buf[i++] = (char)ch; // cast 'ch' as 'char'
        ch = fgetc(fp);
    }
    buf[i] = '\0';
    // returns true if EOF is not found
    // i.e. if line-break is the reason to terminate
    return (ch == '\n');
}

int main() {
    // declaring a buffer to hold a single line contents
    char buf[BUF_SIZE];
    char input[] = "input.txt";

    FILE *fp = fopen(input, "r");
    if(fp == NULL) {
        printf("Can not open file\n");
        exit(1);
    }

    int isLineBreak = 0; // by default declaring it false
    do {
        isLineBreak = readLine(fp, buf); // checking end line status
        printf("%s\n", buf);
        // run loop as long as you find line-break
        // break only if you find EOF


    } while(isLineBreak);

    fclose(fp);

    return 0;
}
reyad
  • 1,392
  • 2
  • 7
  • 12