1

I use this code but it doesn't work properly.

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

int main() {
    char line[120], *word;
    int o, row, col, letter, i;
    o = scanf("%s", line);
    row = 0;
    while(o != -1) {
        col = 0;
        while(isprint(line[col])) {
            word = (char *) malloc(sizeof(char)*20);
            for(i=0; i<20; i++) *(word + i) = 0;
            letter = 0;
            while(isalpha(line[col])) {
                *(word + letter) = line[col];
                col++;
                letter++;
            }
            col++;
            printf("%s\n", word);
            free(word);
        }
        row++;
        o = scanf("%s", line);
    }
return 0;
}

For example, I give as input:

can you take a string?

and I take as output:

can
you
take
a
ke
string

I can't find the mistake, but the fact that the output isn't far from what I want means that the mistake is small. Please help me...:)

stavros
  • 23
  • 6
  • 3
    Now would be a good time to start to learn how to use a debugger. Stepping through the code and inspecting your variables as you go should take you to the bug very quickly. – Paul R Jun 14 '13 at 16:53
  • @user1938049 just so you know, `sizeof(char)` is always `1` in c99. or, you're just writing that for clarity – tay10r Jun 14 '13 at 17:03
  • `scanf("%s", line)` ---> ` scanf("%[^\n]%*c", line)`. you hope your code is entered one line. – BLUEPIXY Jun 14 '13 at 17:27

3 Answers3

2

That's rather complicated. Why don't you just separate all the consecutive non-whitespace substrings by whitespace characters?

char buf[LINE_MAX];
fgets(buf, sizeof(buf), stdin);

char *end;
const char *seps = " \t\r\n\f\v";
char *p = strtok_r(buf, seps, &end);
printf("%s\n", p);
while (p = strtok_r(NULL, seps, &end))
    printf("%s\n", p);

Some more advice:

  • Don't reinvent the wheel. Use the standard library instead of rolling your own string handling (et al.) functions. They facilitate your life, they're guaranteed to be correct (at least in the case of a reasonably high-quality implementation) and they make your code shorter, thus more readable.

  • Do prefer automatic arrays over malloc() when only local (function-scope) storage is needed. Variable-length arrays are standard since C99, so you don't even need to constrain yourself to constant integer expressions when specifying the size of the array.

  • But if you decide to use malloc(), then at least don't cast its return value.

Community
  • 1
  • 1
  • The compiler didn't take this code because "[Error] 'strtok_r' was not declared in this scope". Why? – stavros Jun 14 '13 at 17:06
  • @user1938049 It's Posix, not standard C. What OS are you using? H2CO3, don't forget about `'\v'` : ) – jerry Jun 14 '13 at 17:32
1

May I suggest a slightly better approach to your code?

A known safe way to take input without complicating things is using fgets (as pointed out already).

fgets allows you to specify how many characters you take from the console so that you don't go over the limit of your buffer.

You can use fgets for user input (using the stdin pointer) or reading from a file (by supplying a file handle in place of stdin).

Here's an example of how you can simplify your logic:

#include <stdio.h>

int main()
{
    char input [100];

    /* the [0] bit checks if only a newline has been entered thereby ignoring empty lines */
    /* we also check if fgets returns NULL, which may lead to undefined behavior if ignored */
    while(fgets(input, 100, stdin) != NULL && input[0] != '\n') 
    {
        int i = 0;                       /* this counter keeps track of the current char in the input */
        int w = 0;                       /* keep track if we are in a word, fixes printing newline for each white line */
        while(input[i] != '\0')          /* while we're not at the end of the string */
        {
            switch(input[i])
            {
                case ' ':                /* if the character is any of the below then print newline */
                case '\t':
                case '\r':
                case '\f':
                case '\v':
                case '\n':
                if (w) { w = 0; printf("\n"); } 
                break;
                default:
                if (!w) { w = 1; }
                printf("%c", input[i]);  /* otheriwse print the character itself */
            }
            i++;
        }
    }

    return 0;
}
Nobilis
  • 7,310
  • 1
  • 33
  • 67
  • 1
    There's one big issue and a couple small (or unimportant) ones with this code. First, you've got potential undefined behavior here. Second, you're only considering spaces as word separators and printing the other whitespace characters. Finally, this will output multiple newline characters if there are consecutive spaces. – jerry Jun 24 '13 at 15:32
  • Fair comments, I've tried to address them all while keeping the logic similar (I've tried to limit the number of standard library functions). – Nobilis Jun 24 '13 at 16:59
0

You seem to think that o = scanf("%s", line); will bring in the entire line. This is incorrect, it will only read the first word. Buffer overflows and style issues aside, your whole program can essentially be condensed to:

#include <stdio.h>

int main() 
{
    char line[120];
    while(scanf("%s", line) != -1)
    {
        printf("%s\n", line);
    }
    return 0;
}

Input:

can you take a string?

Output:

can
you
take
a
string?

If you really only want alphabetic characters, you'll have to describe whether other non-whitespace characters are also considered word delimiters or if they're also ignored. For instance, is word1word printed as word twice or as wordword?


Edit:

Assuming you want to completely ignore non-letters, try this:

#include <stdio.h>
#include <ctype.h>

void PrintOnlyLetters(char word[])
{
    int i;
    int count = 0;

    for(i = 0; word[i] != '\0'; i++)
    {
        if(isalpha(word[i]))
        {
            count++;
            printf("%c", word[i]);
        }
    }

    if(count > 0)
    {
        printf("\n");
    }
}

int main() 
{
    char word[120];

    while(scanf("%119s", word) > 0)
    {
        PrintOnlyLetters(word);
    }

    return 0;
}

Input:

can yo4u t@ke one (/1) or more string(s)?

Output:

can
you
tke
one
or
more
strings

Note that while this is simple and does what you want without the read from uninitialized memory, it's still brittle. For instance, it will break up words longer than 119 characters (a change to remove the buffer overflow from your code).

Community
  • 1
  • 1
jerry
  • 2,581
  • 1
  • 21
  • 32
  • Thank you. I didn't know that. However, if I want only letters(not "string?") what I have to do? – stavros Jun 14 '13 at 17:22
  • @user1938049 Simultaneous edit and comment : ) Take a look at the question I asked at the end. By the way, if this is important, note that the other solutions also don't ignore non-letters. – jerry Jun 14 '13 at 17:24
  • @stavros after looking at your original code again, it seems you're just tossing out non-letters (as opposed to treating them as word separators). I've updated my answer accordingly. – jerry Jun 24 '13 at 15:39