#fgets() somehow applying ghost character to string

140 messages · Page 1 of 1 (latest)

bleak citrus
#
bool spelledCorrectly(char* str) {

    // for(int i = 0; i < 50; i++) printf("Word at %i: %s\n", i, wordlist[i]);

    for(int i = 0; i < 50; i++) {

        // printf("Word in list of length %li: %s\nWord to check of length %li: %s\n", strlen(wordlist[i]), wordlist[i], strlen(str), str);
        printf("Verbose list word: ");
        printStringVerbose(wordlist[i]);
        printf("Verbose input word: ");
        printStringVerbose(str);

        if(!strcmp(str, wordlist[i])) return true;
    
    }

    return false;

}

I was finding that my string in the "wordlist" (basically a dictionary read from a file) is always 1 character longer than the words that are passed in by the user. I wrote a function printStringVerbose() that will print out \0 and \n visibly, and when doing so, I get a very odd result.

kind nacelleBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question run !howto ask.

bleak citrus
#

As you can see, the word from the user input is printing just fine (with the null terminator at the end). But the one in the wordlist has some sort of char that makes it overwrite the next line? I have no idea what's going on. Can someone help?

#

I checked if it's an EOF character and it isn't

#

I was originally thinking this was a problem due to the trimLineBreak() function that I wrote to get rid of any \n at the end, but both the user input and the dictionary words go through it, so I don't think the result should be different given the same input

tidal sandal
#

where is wordlist array declared?

#

Show printStringVerbose function too, with the code you provided it's not that easy to help

bleak citrus
# tidal sandal where is `wordlist` array declared?

Here

char wordlist[MAX_WORDS][MAX_WORD_LEN];
// The word list from the dictionary file

void startPromptingForWords();
void populateWordlist(char* wordlistFileName);
bool spelledCorrectly(char* str);
char* trimLineBreak(char* str);
void setToLowerCase(char** str);
void printStringVerbose(char* str);

int main(int argc, char** argv) {

    char* fileName;
    if(argc < 2) { printf("[ERROR] An input file must be supplied\n"); exit(-1); }
    if(argc > 2) { printf("[ERROR] Only one input file may be supplied\n"); exit(-1); }
    
    fileName = argv[1];
    populateWordlist(fileName);

    startPromptingForWords();

}
#

It's a global var

#
void populateWordlist(char* wordlistFileName) {
    // Copies the supplied file into the wordlist array

    FILE* wordlistFile;

    if((wordlistFile = fopen(wordlistFileName, "r"))) {
        // If the supplied file exists

        for(int i = 0; fgets(wordlist[i], MAX_WORD_LEN, wordlistFile); i++) {

            strncpy(wordlist[i], trimLineBreak(wordlist[i]), MAX_WORD_LEN);
        
        }
        // Copy each line of the file to the wordlist array, trimming line breaks from the end of each word

    } else { printf("[ERROR] The supplied input file does not exist\n"); exit(-1); }

}
#

This is where it gets its words

#
void printStringVerbose(char* str) {

    int len = strlen(str);

    for(int i = 0; i <= len; i++) {

        if(str[i] == '\0') printf("\\0");
        else if(str[i] == '\n') printf("\\n");
        else if(str[i] == EOF) printf("EOF");
        else printf("%c", str[i]);

    }

    printf("\n");

}

And here is printStringVerbose()

slender bane
#

so the list always contains a whole extra word?
or each word is an extra character long?

bleak citrus
#

But I cannot for the life of me figure out what type of char it is

bleak citrus
#

But if I print out the strlen() of both the "input" word and the list word, the list word is always one character longer

slender bane
#

do you know the difference between Windows and Linux line endings?
\r\n and \n

bleak citrus
#

This is running on WSL Ubuntu

#

Though I did not know that

slender bane
#

this might help you: you could try changing the fopen type to "rb" instead of "r"

#

the b flag tells the OS to open it in binary mode which stops the file having line endings automatically converted

tidal sandal
#

What does the startPromptingForWords function do?

bleak citrus
bleak citrus
#
void startPromptingForWords() {
    // Prompts the user for words using stdin and gives them spelling recommendations

    char* word = malloc(MAX_WORD_LEN * sizeof(char));

    while(true) {

        printf("\nWord: ");
        fgets(word, MAX_WORD_LEN, stdin);
        word = trimLineBreak(word);
        setToLowerCase(&word);
        printf("Word is spelled %s\n", (spelledCorrectly(word) ? "correctly" : "incorrectly"));
        printf("%s", word);

    }

}
slender bane
#

for(int i = 0; i <= len; i++) {
you loop up to (equal to) len, so the loop includes the null terminator?

tidal sandal
bleak citrus
#

Should just grab each word from the user

bleak citrus
slender bane
bleak citrus
bleak citrus
#

So it can make sure the string is actually null terminated

slender bane
#

i can only guess i think you have an off-by-one bug somewhere. probably to do with the buffer length values

bleak citrus
#

Well I can trace it back

#

lemme try something

#
void populateWordlist(char* wordlistFileName) {
    // Copies the supplied file into the wordlist array

    FILE* wordlistFile;

    if((wordlistFile = fopen(wordlistFileName, "rb"))) {
        // If the supplied file exists

        for(int i = 0; fgets(wordlist[i], MAX_WORD_LEN, wordlistFile); i++) {

            strncpy(wordlist[i], trimLineBreak(wordlist[i]), MAX_WORD_LEN);

            printf("The word is: %s\nOf length: %li\n", wordlist[i], strlen(wordlist[i]));
            printf("With hidden chars, it is: ");
            printStringVerbose(wordlist[i]);
        
        }
        // Copy each line of the file to the wordlist array, trimming line breaks from the end of each word

    } else { printf("[ERROR] The supplied input file does not exist\n"); exit(-1); }

}
#

That is the result of adding this print block

#

So it is occurring when originally populating the list

slender bane
#

ok

bleak citrus
#

It doesn't occur in trimLineBreak(), at least not directly, as far as I can tell

#

I was worried it was in that function but nope

slender bane
#

web is 3 chars, not 4

bleak citrus
#

Yeah I know

#

I don't know what char is in there

#

I think that whatever char it is, it's between the last char and the \n

slender bane
#

the file itself has line breaks?

#

it is pulling those in from the file, that is all

bleak citrus
#

This is if I move the print above the trim

#

Yes

slender bane
#

fgets "Parsing stops if a newline character is found, in which case str will contain that newline character"

#

so i think the problem is in trimLineBreak

#

a string needs strlen(str)+1 buffer size

#

int len = strlen(str); doesn't seem right

bleak citrus
#

trimLineBreak() does not mess with a manually-created string

#

I tested it

bleak citrus
#

Oh boy

#

I see something

slender bane
#

that is my suggestion

bleak citrus
#

Though that might just be a debugger thing? cuz VSC is running on Windows

slender bane
#

wait, your paste still has the file in binary mode

#

"rb"

bleak citrus
#

yes

slender bane
#

change it back to "r" if you are reading text. i only suggested "rb" because it will show you the literal file contents

#

you seem to have a Windows text file

bleak citrus
#

yeah I think so

#

Well I mean it was downloaded from Windows and pasted into WSL

slender bane
#

ok

#

did you try this? it looks like you have an off-by-one bug to me

#

strlen returns the length of the string and not the buffer size

#

strlen("A") is 1, but to fit "A" in a buffer you need to include the null terminator, so you need strlen("A")+1 bytes of buffer

#

in fact, the strncpy function take the number of characters not the buffer size

#
char* trimLineBreak(char* str) {
    // Trims any trailing line breaks from a string
 
    int oldLen = strlen(str) + 1;

    int newLen = oldLen - 1;
 
    char* newStr = malloc(newLen * sizeof(char));
    strncpy(newStr, str, newLen - 1);
 
    return newStr;
}```
bleak citrus
slender bane
#

that is what i suggest. note the strncpy is -1 too

slender bane
#

can you paste it again. i can try to compile it

bleak citrus
#

I think it's the \r??

slender bane
#

the \r should be translated if you open the file with fopen and use "r" instead of "rb"

bleak citrus
slender bane
#

your code makes no sense as it is

bleak citrus
#

I did ???

#

It's in the paste

slender bane
#

strlen - length of string
malloc - length of buffer
strncpy - length of string

#

this is the problem i think

bleak citrus
slender bane
#

ok, i see. let me compile it

#

yes

#

i will see

bleak citrus
#

any news

slender bane
#

yes

slender bane
# bleak citrus any news
char* trimLineBreak(char* str) {
    // Trims any trailing line breaks from a string
 
    int oldLen = strlen(str) + 1;

    int newLen = oldLen - 1;
 
    char* newStr = malloc(newLen * sizeof(char));
    strncpy(newStr, str, newLen - 1);

    newStr[newLen - 1] = '\0';
 
    return newStr;
}
#

i debugged it and saw that the strncpy was hitting the end of the string without finding the null terminator
according to the documentation "No null-character is implicitly appended at the end of destination if source is longer than num."
so i explicitly padded it with \0 and that seems to fix it

bleak citrus
#

Null terminator strikes again

slender bane
#

yeh

#

let me know if that fixes it

bleak citrus
#

apparently not

slender bane
#
        for(int i = 0; fgets(wordlist[i], MAX_WORD_LEN, wordlistFile); i++) {

            printf("The word is: %s\nOf length: %li\n", wordlist[i], strlen(wordlist[i]));
            printf("With hidden chars, it is: ");
            printStringVerbose(wordlist[i]);

            strncpy(wordlist[i], trimLineBreak(wordlist[i]), MAX_WORD_LEN - 1);
        
        }
#

i forgot you also need strncpy(wordlist[i], trimLineBreak(wordlist[i]), MAX_WORD_LEN - 1);

#

MAX_WORD_LEN - 1

#

again, due to fgets and malloc needing buffer length and strncpy needing character count

bleak citrus
#

I'll pastebin again rq

slender bane
#

ok

bleak citrus
slender bane
#

seems to work for me

bleak citrus
#

Maybe I should make a new text file?

slender bane
#

yes, it is the Windows vs Linux line endings

bleak citrus
#

Ok I think that it has to do with Windows fucking up everything as usual

#

But t does still cut off the last char of the last word in the list

slender bane
#

ok i guess that's a separate issue

slender bane
bleak citrus
#

Also I'm not sure how I'mma get the wordlist files I need to use to not have the \r

slender bane
#

i had an empty newline at the end of my file but i removed it

bleak citrus
slender bane
#

yes i did in the screenshots

bleak citrus
#

So you removed it and it didn't mess with anything?

slender bane
#

yes

bleak citrus
#

hmm

slender bane
#

you could detect and convert "word\r\n" to "word"

#

so that it works for both formats

#

i thought that fopen "r" is meant to auto convert line endings. i don't know why it doesn't

#

you could ask someone else since that is a separate problem

#

i think the buffer length issue is fixed now

bleak citrus
#

I fixed the issue by adding a check to make sure it's got a newline

#

if it doesn't then it just returns the original string

bleak citrus
#

Thanks for the help

#

!solved