0

I have ran into a problem while learning to program in C. I am creating a program to translate a text into "rövarspråket" (you don't need to understand what it is to understand my problem). The program works great if I only insert one word, but when I add a space it just stop producing the output. Here'sn example:

If I insert "hello world", I want the output to be "hohelollolo wowororloldod", but instead it outputs "hohelollolo♀". Here's the code:

int main(){

    char inputText[100], outputText[300];
    int inputLength, ipTextNum, opTextNum;

    scanf("%s", &inputText);
    inputLength = strlen(inputText);

    for(ipTextNum = 0; ipTextNum < inputLength; ipTextNum++){

        if(inputText[ipTextNum] == 'a' || inputText[ipTextNum] == 'e' || inputText[ipTextNum] == 'i' || inputText[ipTextNum] == 'o' || inputText[ipTextNum] == 'u' || inputText[ipTextNum] == 'y' || inputText[ipTextNum] == 'å' || inputText[ipTextNum] == 'ä' || inputText[ipTextNum] == 'ö'){
            outputText[opTextNum] = inputText[ipTextNum];
            opTextNum++;    
        }

        else {
            outputText[opTextNum] = inputText[ipTextNum];
            outputText[opTextNum+1] = 'o';
            outputText[opTextNum+2] = inputText[ipTextNum];
            opTextNum += 3; 
        }
    }

    printf("%s", outputText);


    return 0;
}

If I also add inputText[ipTextNum] == ' ' to the if statement, it outputs the same thing as before, but without the ♀. Does anyone have a clue what the problem might be? I've tried to print the input, but it seems like all the text after the whitespace is terminated there too. Is whitespace even allowed in strings and if not, how do I work around this?

user3600338
  • 119
  • 1
  • 10
  • What have you done to debug this? It should be easy to insert printf statements here and there to see what's going on inside, if you don't have an interactive debugger. – Hot Licks May 04 '14 at 01:19
  • After safely reading in the input with `scanf` make sure your output doesn't overflow the available space- right now you are not testing that `opTextNum<300` and will eventually catch up with you. **always** check your inputs, and your array boundaries. – Floris May 04 '14 at 01:36

4 Answers4

2

issue is here:

scanf("%s", &inputText);

there are actually two issues.. You want it to read an entire line I assume, so we don't use %s, we use "all characters except \n", then read the \n. The other issue is the &inputText.

scanf("%s", inputText);

is how it should be.. but this adds the entire line.

scanf("%[^\n]\n", inputText);

Go ahead and do your parsing on that, as long as you dont overflow!

phyrrus9
  • 1,441
  • 11
  • 26
  • 1
    Good simple solution, though I have not tested it. Makes sense though. +1 – DerStrom8 May 04 '14 at 01:26
  • Good answer. Should also mention that `opTextNum` needs to be initialized to 0, and the output string needs a null terminator at the end. – user3386109 May 04 '14 at 02:38
  • Thank you, it worked, although I had to swap the "%[^\n]\n" for "%[^\n]". Does the ^\n means that if enter is pressed, the string will stop? – user3600338 May 04 '14 at 09:52
  • what would happen is, it reads everything except \n, then next time, the only char is \n, so empty string. – phyrrus9 May 04 '14 at 16:35
1

scanf really shouldn't be used for strings with spaces because it does not accept them. You should really use fgets instead. See this answer for more information:

https://stackoverflow.com/a/1248017/2990189

Here is an example (from the above answer) to show how fgets works:

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

/* Maximum name size + 1. */
#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) {
    /* Allocate memory and check if okay. */
    char *name = malloc (MAX_NAME_SZ);
    if (name == NULL) {
        printf ("No memory\n");
        return 1;
    }

    /* Ask user for name. */
    printf("What is your name? ");

    /* Get the name, with size limit. */
    fgets (name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */
    if ((strlen(name)>0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */
    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */
    free (name);
    return 0;
}
Community
  • 1
  • 1
DerStrom8
  • 1,311
  • 2
  • 23
  • 45
1

As well as ensuring that the input does not overflow the buffer as other posters pointed out; another major issue is that you never null-terminate your output.

The printf("%s" knows when to stop outputting characters because it encounters a null byte. But you never wrote a null byte on the end of your output, which explains why there is a garbage character after it (and then you happened to have a null byte by chance in your buffer after the garbage).

After your loop, add in outputText[opTextNum] = 0; before the printf.

Of course, you also need to make sure your loop always stops with opTextNum < sizeof outputText being true. Since input has max string length 99 , then the max output that can be written is 3*99+1, so you're safe as things stand. If you decide to change your algorithm later you'll need to think about this again though.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • +1 for all good points. I think that further effort needs to be expended to deal with the wide characters, and testing for space. – Floris May 04 '14 at 04:00
0

Superficially, your problem is the use of scanf - it stops at the first space it encounters. Using fgets is usually the answer - and that might have been that. There was another problem in that you did not terminate your string (which led to the interesting last character - quite by chance, I must add), and you did not check for overflow in either the input or output buffers. Most of these points were made in other answers.

However - the problem is quite a bit more interesting, and harder, because you are using "non ascii" characters in your input. I am guessing your robbers have Scandinavian roots. This means that you not only need to worry about whitespace - you need to be using "wide characters".

I took the liberty of rewriting your example in "wide" form - meaning that it uses

fgetws   - the wide equivalent of fgets
wprintf  - the wide equivalent of printf
wcschr   - the wide equivalent of strchr

As well as "wide" strings like L"wide string", "wide" characters like L'å', and "wide" format specifier %ls.

A stylistic problem with your code is that really long if statement that consisted of a long series of OR-ed comparisons with individual characters. You can do one of three things:

  1. format more clearly with carriage returns and line continuations:

 

if( inputText[ipTextNum] == L'a' || \
    inputText[ipTextNum] == L'e' || \
    inputText[ipTextNum] == L'i' || \
    inputText[ipTextNum] == L'o' || \
    inputText[ipTextNum] == L'u' || \
    inputText[ipTextNum] == L'y' || \
    inputText[ipTextNum] == L'å' || \
    inputText[ipTextNum] == L'ä' || \
    inputText[ipTextNum] == L'ö')

Note: you did not test for space, so space is turned into o which is not what you want according to your description.

 2. Replace the whole thing with wcschr which looks for a character in a string; by searching for the character in the string of vowels (this is what I did in the code below - including test for space); or

  3. You could also create a function for yourself like

int isVowel(wchar_t c) {
  return wcschr("aeiouyåäö ", c)!=0;
}

To make it even more compact/readable.

Anyway - here is the complete, annotated code:

#include <stdio.h>  // <<<< dont forget your header files
#include <wchar.h>  // <<<< to be able to use 'wide' characters: ö etc

// define the max size of input and output string:
#define IP_MAX 100
#define OP_MAX 300

int main(void) { // <<<<< use the correct function signature

    wchar_t inputText[IP_MAX], outputText[OP_MAX];
    int inputLength, ipTextNum, opTextNum=0; // <<< initialize opTextNum to zero!!

//    scanf("%s", &inputText);  // <<<< triple NO:
                                // 1) scanf stops at the first space
                                // 2) if the input is very long you overwrite your buffer
                                // 3) you are passing the POINTER to the pointer to char;
                                //    you should use inputText not &inputText

    fgetws(inputText, IP_MAX, stdin); // read at most IP_MAX characters into inputText including '\0'
    inputLength = wcslen(inputText);  // length of wide C string
    inputText[--inputLength]='\0';    // strip carriage return
    //    printf("The length of the string entered is %d\n", inputLength);
    wprintf(L"you entered \n'%ls'\n", inputText);

    for(ipTextNum = 0; ipTextNum < inputLength && opTextNum < OP_MAX-3; ipTextNum++) {
       if(wcschr(L"aeiouyåäö ", inputText[ipTextNum])) { // <<< include test for space too
            outputText[opTextNum] = inputText[ipTextNum];
            opTextNum++;
        }    
        else {
            outputText[opTextNum] = inputText[ipTextNum];
            outputText[opTextNum+1] = 'o';
            outputText[opTextNum+2] = inputText[ipTextNum];
            opTextNum += 3;
        }
    }
    outputText[opTextNum]=L'\0'; // nul terminate the string

    wprintf(L"The robber said:\n'%ls'\n\n", outputText);

    return 0;
}

Output:

hello world
you entered 
'hello world'
The robber said:
'hohelollolo wowororloldod'
Floris
  • 45,857
  • 6
  • 70
  • 122