简体   繁体   中英

Reading from a file, C

I am currently working on aa 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.

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, 

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM