0

I'm facing an issue while trying to resolve a particular problem in C. User input must be read in encrypted form and the output must be the decrypted message, that is, it is a replacement cipher.The problem is that when I read the first input, the program loops printing the decrypted message, it doesn't ask to read other inputs.
I'm really confused about this as I was told that I should use EOF, something I'm not very knowledgeable about. I could even solve it another way, but I need to implement the EOF, and because I don't know that, I'm having difficulty solving the exercise.

I am aware that the code I created is not the best for the resolution, however, it might work if someone helps me with this loop issue.

Source Code

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

void alterChar(int i, char phrase[])
{
    for (i = 0; phrase[i] != '\0'; i++)
    {
        if (phrase[i] == '@')
        {
            phrase[i] = 'a';
        }
                
        if (phrase[i] == '&')
        {
            phrase[i] = 'e';
        }
                
        if (phrase[i] == '!')
        {
            phrase[i] = 'i';
        }
                
        if (phrase[i] == '*')
        {
            phrase[i] = 'o';
        }
                
        if (phrase[i] == '#')
        {
            phrase[i] = 'u';
        }
                
        printf("%s\n", phrase);
    }
}

int main()
{
    int i;
    char phrase[256];
    
    while (scanf("%[^\n]s", phrase) != EOF)
    {
        ///scanf(" %[^\n]s", phrase);
    
        alterChar(i, phrase);
    }
    
    return 0;
}
Shi Nha
  • 37
  • 7
  • 1
    First of all, there's no [`scanf`](https://en.cppreference.com/w/c/io/fscanf) format `%[]s`. There is `%[]` though (note the lack of the `s` at the end). Also read about what [`scanf` returns](https://en.cppreference.com/w/c/io/fscanf#Return_value). Lastly, to read full lines use [`fgets`](https://en.cppreference.com/w/c/io/fgets) instead. – Some programmer dude Dec 12 '21 at 12:04
  • 2
    And why do you pass the (uninitialized) variable `i` as an argument to the function? Why not define it locally inside that function? – Some programmer dude Dec 12 '21 at 12:05
  • 1
    Security tip: do not use `scanf` or `fgets` with untrusted input (and all input should be considered untrusted unless you have a good reason to trust it).... Depending on your use-case, some really fun stuff can start to happen when you have a few megabytes (or a giga or two) without any EOL (`\n`)... – Myst Dec 12 '21 at 13:22
  • @Myst: In contrast to `scanf` and `gets`, I see no reason not to use `fgets` with untrusted input. Please elaborate on why this function should not be used and which function you would recommend instead. – Andreas Wenzel Dec 12 '21 at 18:29
  • @AndreasWenzel - yes, you're technically right about `fgets` being considered safe. I was thinking of `gets`... though at the same time, calling `fgets` in a loop without making sure you have enough resources to process the data can get pretty interesting too. Anyway, just my 2 cents - IMHO it's better to parse the data on your own. – Myst Dec 12 '21 at 23:30

2 Answers2

0

The line

while (scanf("%[^\n]s", phrase) != EOF)

is wrong, for several reasons:

  1. You seem to be undecided on whether to use the %s or the %[] conversion format specifier. The character s is not part of the %[] format specifier. Therefore, when using your format string, scanf will attempt to match a literal s. This is probably not what you want, so it should be removed from the format string.

  2. If scanf is unable to match any characters because the line is empty, it will return 0 and will not touch the contents of phrase. However, you are incorrectly treating the return value of 0 as success.

  3. If the user enters more than 255 characters, then you will be writing to the array phrase out of bounds, causing undefined behavior (i.e your program may crash). Therefore, you should limit the number of matched characters to 255 characters, so that the function won't write more than 256 characters (including the terminating null character of the string).

For the reasons stated above, you should change the line to the following:

while ( scanf( "%255[^\n]", phrase ) != 1 )

Also, this scanf statement will not consume the newline character from the input stream, so the next function call to scanf will fail. Therefore, you must call a function to consume the newline character from the input stream, for example by calling getchar, or by telling scanf to consume all whitespace input beforehand, like this:

while ( scanf( " %255[^\n]", phrase ) != 1 )

Alternatively, you could use the function fgets instead (which I recommend), as that function will also consume the newline character and write it to the string.

while ( fgets( phrase, sizeof phrase, stdin ) != NULL )

When using the function fgets, you should also verify that an entire line was read in, for example by writing the following code afterwards:

//verify that entire line was read in
if ( strchr( phrase, '\n' ) == NULL )
{
    printf( "line to long for input buffer!\n" );
    exit( EXIT_FAILURE );
}

Note that if you decide to use the function fgets, you will have an additional newline character at the end of the line, instead of only a '\0', so in the function alterChar, you will have to change the line

for (i = 0; phrase[i] != '\0'; i++)

to

for (i = 0; phrase[i] != '\n'; i++)

Another problem in your code is that the line

alterChar(i, phrase);

does not make sense. You are passing the value of an uninitialized variable to the function.

You should probably change the function prototype from

void alterChar(int i, char phrase[])

to

void alterChar( char phrase[] )

and maybe also give the function a better name, as it does not deal with single characters, but whole strings, and also prints it:

void alterAndPrintPhrase( char phrase[] )

Also, in that function, you should move the line

printf("%s\n", phrase);

outside the loop.

After applying all the fixes mentioned above, your code should look like this:

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

void alterAndPrintPhrase( char phrase[] )
{
    for ( int i = 0; phrase[i] != '\n'; i++ )
    {
        if (phrase[i] == '@')
        {
            phrase[i] = 'a';
        }
                
        if (phrase[i] == '&')
        {
            phrase[i] = 'e';
        }
                
        if (phrase[i] == '!')
        {
            phrase[i] = 'i';
        }
                
        if (phrase[i] == '*')
        {
            phrase[i] = 'o';
        }
                
        if (phrase[i] == '#')
        {
            phrase[i] = 'u';
        }
    }

    printf( "%s\n", phrase );
}

int main()
{
    char phrase[256];
    
    while ( fgets( phrase, sizeof phrase, stdin ) != NULL )
    {
        //verify that entire line was read in
        if ( strchr( phrase, '\n' ) == NULL )
        {
            printf( "line to long for input buffer!\n" );
            exit( EXIT_FAILURE );
        }

        alterAndPrintPhrase( phrase );
    }
    
    return 0;
}

This program has the following behavior:

Input:

Th!s !s @ t&st.

Output:

This is a test.
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
-2

i think just putting the printf outside of the loop help and use feof (fetch en of file) to know where you are.

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

void alterChar(char phrase[]) {
    for (int i = 0; phrase[i] != '\0'; i++) {
        if (phrase[i] == '@') {
            phrase[i] = 'a';
        }

        if (phrase[i] == '&') {
            phrase[i] = 'e';
        }

        if (phrase[i] == '!') {
            phrase[i] = 'i';
        }

        if (phrase[i] == '*') {
            phrase[i] = 'o';
        }

        if (phrase[i] == '#') {
            phrase[i] = 'u';
        }
    }
    printf("%s\n", phrase);
}

int main() {
    char phrase[256];

    while (!feof(stdin)) {
        scanf(" %[^\n]s", phrase);
        alterChar(phrase);
    }

    return 0;
}
LTBS46
  • 205
  • 1
  • 10
  • 4
    Why putting `feof` in a while loop is wrong - https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong – Ed Heal Dec 12 '21 at 12:14
  • You also haven't done anything about the two most serious problems in the code. And one should *always* check what `scanf` returns. – Some programmer dude Dec 12 '21 at 14:14
  • `while (!feof(stdin)) { scanf(" %[^\n]s", phrase);` is poor code 1) `scanf()` return not tested. 2) No width limit to `"%[^\n]"` 3) Worthless `"s"` after `"%[^\n]"`. LTBS46. who or what text suggested such poor code? – chux - Reinstate Monica Dec 12 '21 at 15:54
  • If `scanf` fails due to encountering end-of-file, then you will be processing the (modified) last line twice. See the link in the first comment for an explanation of why this is happening. – Andreas Wenzel Dec 12 '21 at 19:23