I think one of the reasons authors of books about programming in C make heavily use
of the function scanf() is that is readily available and easy to use for the new learner.
That's all good and dandy, however when writers of this books get to the more advanced
topics, they fail to remember to go back and say: "by the way, forget about using scanf().
It's a function with a lot of overhead, and full of "gotchas".

I have been looking to make a function of my own that I can use instead of the scanf(), and include in a header of file.

I think, that thanks to you guys, I have a good try here. I intend to create another for reading ints, and reading floats.

What do you think?. Do you see a better way, perhaps?.

I don't like the way I have to pass as a parameter the size of the char array, but I don't
know any other way, besides entering the number amount of elements directly. And that makes it less maintainable.

/*
 * read_string.c
 * test a function that reads string input and if needed clears the stdin buffer
 */

#include <stdio.h>

int read_string(char *string, int max); /* prototype */

int main(void)
{
    char answer[30];  /* a array to store the string up to 29 characters + '\0' */

    printf("Enter some text: ");
    read_string(answer, sizeof(answer)/sizeof(char)); /* call to read a string input */
    
    puts(answer);  /* display the answer for test purpose */
    
    getchar();  /* pause the program until a character is entered */
    return(0);
}

/* read_string
    parameters:
        char *string -> pointer to a string
        int   max -> maximum number characters to be read */
int read_string(char *string, int max)
{
    if(fgets(string, max, stdin) != NULL)  /* if input has been entered execute next block */
    {
        char *newline = strchr(string, '\n'); /* look for the "newline" character and set the pointer to it */
        if(newline != NULL)  /* Does the pointer points to a character that is not the NULL? */
       {
           *newline = '\0'; /* the value that newline points to is replaced with a string terminator */
        }
        else /* if above is not true there's extra characters in the stdin buffer, watch out! */
        {
         /* get rid off as many characters as there are in the stdin file waiting to screw-up your code */
            char ch;
            do
            {
                ch = getchar();
            }
            while(ch != '\n' && ch != EOF); /* finish when is not longer true */
        } /* for the else */
    }  /* for the first if */
}    /* to close the function */

Recommended Answers

All 18 Replies

I think one of the reasons authors of books about programming in C make heavily use of the function scanf() is that is readily available and easy to use for the new learner.

But it's so dang unsafe and persnickety! :confused:

That's all good and dandy, however when writers of this books get to the more advanced topics, they fail to remember to go back and say: "by the way, forget about using scanf().
It's a function with a lot of overhead, and full of "gotchas".

And that's why I'd like them to stop teaching it. ;)

I have been looking to make a function of my own that I can use instead of the scanf(), and include in a header of file.

I think, that thanks to you guys, I have a good try here. I intend to create another for reading ints, and reading floats.

What do you think?. Do you see a better way, perhaps?.

Great job! Offhand, the only thing I'd do differently is instead of

char *newline = strchr(string, '\n'); /* look for the "newline" character and set the pointer to it */
if(newline != NULL)

I'd use

int newline = strlen(string) - 1;
if (string[newline] == '\n')

I believe it's faster to get the length of a string than to search for a character, and the \n will always be the last character.

I don't like the way I have to pass as a parameter the size of the char array, but I don't know any other way, besides entering the number amount of elements directly. And that makes it less maintainable.

Since you have no idea how many characters the user will enter, and you don't want to blow your array, passing the size is the best way to control the input.

Also, in C your statement char *newline = strchr(string, '\n'); is technically an illegal statement. You have to define the variable before any executable statements in a code block (between { }'s).

One more thing about readability, for multi-line comment, always format them something like this to make them stand out:

/* read_string
 *  parameters:
 *      char *string -> pointer to a string
 *      int max -> maximum number characters to be read 
 */

I think one of the reasons authors of books about programming in C make heavily use
of the function scanf() is that is readily available and easy to use for the new learner.

Deceptively so, and that's the biggest problem. ;) Instead of making the effort of reading a line, parsing out the values that you want, and finally converting the strings into the desired types, scanf() does it all in one swell foop. ;) Even if you shouldn't use scanf() in production, it makes learning much easier. In theory at least. There's always the problem of unlearning bad habits and hitting the myriad subtle problems with scanf() that people find when they aren't familiar with C streams and scanf()'s quirks. :sad:

I have been looking to make a function of my own that I can use instead of the scanf(), and include in a header of file.

scanf() tries to do too much. You can focus your input better with getchar(), fgets(), strtol(), and strtod(). Those four functions are all standard and cover the range of types that you would use scanf() for. :) The only problem is that they're more verbose solutions.

What do you think?. Do you see a better way, perhaps?.

I don't. Nice work! :) For a sister function you should try reading a string of any length instead of flushing the stream when the buffer is full. The two work well as a pair because sometimes you want fgets() functionality and sometimes you want to read an unbounded line.

Also, in C your statement char *newline = strchr(string, '\n'); is technically an illegal statement. You have to define the variable before any executable statements in a code block (between { }'s).

It is defined before any executable statements in the code block, isn't it? :confused:

@Aia

You really need to cut down the commenting a bit -- its overwhelming.

With practice you will surely come to know when the documentation is getting out of hand -- your code is almost 50 % documentation and that too sprinkled throughout the code which makes it difficult to read the code.

Also it would be better if you would write documentation in a format which could be used for auto generating documentation for the entire project. Read this.

Also, in C your statement char *newline = strchr(string, '\n'); is technically an illegal statement.

It is defined before any executable statements in the code block, isn't it? :confused:

Ahhh, so it is. Got caught by a style issue.

You really need to cut down the commenting a bit -- its overwhelming.

With practice you will surely come to know when the documentation is getting out of hand -- your code is almost 50 % documentation and that too sprinkled throughout the code which makes it difficult to read the code.

Completely disagree! A better form, maybe, but it's refreshing to see commented code. The more comments the better -- as long as they are descriptive & to the point and not just rambling thoughts. And his comments help understanding, they aren't extraneous.

Also it would be better if you would write documentation in a format which could be used for auto generating documentation for the entire project. Read this.

Unless you have a system to actually read the code for the documentation, it's not necessary. In all my years in programming I've only used one once. Personally, I feel it's unnecessary unless your job requires it. Without the generator it's a waste of time.

A better form, maybe, but it's refreshing to see commented code. The more comments the better -- as long as they are descriptive & to the point and not just rambling thoughts.

Help understanding what and to whom ? If learning I can understand, but in professional setting this kind of code is generally frowned upon. Yet to see a company using *this* kind of coding. Its like "yes our team is creating the next big thing" and "oh btw don't forget to document what strchr is supposed to be doing there"....

But again, if not working for any organization, its a matter of style.

And his comments help understanding, they aren't extraneous.

Wow, take a look at these (don't take this as criticism Aia)

puts(answer);  /* display the answer for test purpose */    
    getchar();  /* pause the program until a character is entered */

Not extraneous you say....

Unless you have a system to actually read the code for the documentation, it's not necessary. In all my years in programming I've only used one once. Personally, I feel it's unnecessary unless your job requires it. Without the generator it's a waste of time.

Again depends on whether you work for an organization, or like to keep it organized. But still is better since you get an upper hand of the automatic documentation being generated for you for a bit of extra effort...

commented: commenting is good -2

@Aia

You really need to cut down the commenting a bit -- its overwhelming.

With practice you will surely come to know when the documentation is getting out of hand -- your code is almost 50 % documentation and that too sprinkled throughout the code which makes it difficult to read the code.

Yeah, I notice that I have the tendency to get over-board with comments. In this case, I also notice that even when in my IDE enviroment all those comments do not look that bad, in the post they look too busy and make the code somehow not very clean.
I accept your guidance that I need to obtain a balance, and problably a better format. The problem is that I try to describe everything that the code is trying to do, many times on lines that are not necesary. But is not the first time I try to go back to something I have done and I don't have an idea why I did it in that way, or it doesn't make sense, especially when I am experimenting with an idea and I leave it half way.
Maybe the good thing about it is that if a beginner with the same question finds this piece of code, for sure it would know what is going on. I hope :-|

Your comments are appreciated and I like that I am being mesured up by profetional standards.

...strtol(), and strtod(). Those [...] functions are all standard and cover the range of types that you would use

I'll make sure I'll look up in the reference what these two do.

For a sister function you should try reading a string of any length instead of flushing the stream when the buffer is full. The two work well as a pair because sometimes you want fgets() functionality and sometimes you want to read an unbounded line.

Sorry, but I get lost in translation..., how do you read a string of any length?. Problably I know if I see it, but..., I can't assosiated with my knowledge.
what's an unbounded line?.

Sorry, but I get lost in translation..., how do you read a string of any length?. Problably I know if I see it, but..., I can't assosiated with my knowledge.
what's an unbounded line?.

If fgets doesn't read a '\n', there are more characters in the stream. Just keep calling fgets and appending the result to your string until there is a '\n'. :) The tricky part is making sure that your string can always hold as many characters as you find. :)

An unbounded line is one you don't specify the length for beforehand. fgets() reads a bounded line and gets() reads an unbounded line. But gets() reads an unbounded line into bounded memory, and that's a huge problem. ;)

Yeah, I notice that I have the tendency to get over-board with comments. In this case, I also notice that even when in my IDE enviroment all those comments do not look that bad, in the post they look too busy and make the code somehow not very clean.

My favorite color scheme has a tendency to hide comments except when I'm looking for them, and as a result I take pains not to comment too much. ;)

I accept your guidance that I need to obtain a balance, and problably a better format. The problem is that I try to describe everything that the code is trying to do, many times on lines that are not necesary. But is not the first time I try to go back to something I have done and I don't have an idea why I did it in that way, or it doesn't make sense, especially when I am experimenting with an idea and I leave it half way.

My three rules for commenting are

  1. Comment things that take more than a moment to understand.
  2. Comment the abstract behaviour of your algorithms.
  3. Comment any unconventional choices you've made.

But is not the first time I try to go back to something I have done and I don't have an idea why I did it in that way, or it doesn't make sense, especially when I am experimenting with an idea and I leave it half way.

No one denies that writing comments for some things which don't make sense to you is a good thing but writing comments for eveything is an overkill and not good. You end up confusing yourself as well as your colleague programmer. Since you are beginning programming you might be tempted to write comments but as time goes on you will automatically realize when comments should come and when they shouldn't. An time you feel like seeing how professional commenting is done, just pick up a open source applications' code or Java's source code and you would know how its done.

Maybe the good thing about it is that if a beginner with the same question finds this piece of code, for sure it would know what is going on. I hope :-|

Ever considered writing tutorials... ;)

The comments in your code might have looked good if they had been pulled out of the code and used to describe the code like the tutorials that is if you really want to help someone or feel that you might forget some things.

Its a pity that Raye didn't do a postmortem of your code to show you that how commenting actually should have been done. He is really good at that -- request him and he won't deny... ;)

Your comments are appreciated and I like that I am being mesured up by profetional standards.

Like we all say "We help those who help themselves.." :D

Oh and as far as the unbounded input is considered, normally, the space is dynamically allocated in chunks (eg. 512 bytes) using malloc and the likes. I guess thats the way in which strings in interpreted languages work. Try exploring that avenue, and you won't be far from your unbounded goal....(pun intended...;))

/*
 * read_string.c
 * test a function that reads string input and if needed clears the stdin buffer
 */

#include <stdio.h>

int read_string(char *string, int max); /* prototype */

int main(void)
{
    char answer[30];  /* a array to store the string up to 29 characters + '\0' */

    printf("Enter some text: ");
    read_string(answer, sizeof(answer)/sizeof(char)); /* call to read a string input */
    
    puts(answer);  /* display the answer for test purpose */
    
    getchar();  /* pause the program until a character is entered */
    return(0);
}

/* read_string
    parameters:
        char *string -> pointer to a string
        int   max -> maximum number characters to be read */
int read_string(char *string, int max)
{
    if(fgets(string, max, stdin) != NULL)  /* if input has been entered execute next block */
    {
        char *newline = strchr(string, '\n'); /* look for the "newline" character and set the pointer to it */
        if(newline != NULL)  /* Does the pointer points to a character that is not the NULL? */
       {
           *newline = '\0'; /* the value that newline points to is replaced with a string terminator */
        }
        else /* if above is not true there's extra characters in the stdin buffer, watch out! */
        {
         /* get rid off as many characters as there are in the stdin file waiting to screw-up your code */
            char ch;
            do
            {
                ch = getchar();
            }
            while(ch != '\n' && ch != EOF); /* finish when is not longer true */
        } /* for the else */
    }  /* for the first if */
}    /* to close the function */

First, you should #include <string.h>. Second, ch needs to be an int. Also your function should return a value. And it is better to use sizeof(answer)/sizeof(*answer) instead of sizeof(answer)/sizeof(char).

Earlier I was trying to futz with the comments a little -- basically don't tell what the code is or is doing, but tell why.

For example, telling us you are declaring an array next to the declaration of an array, or telling us some code is a prototype right after the prototype -- here you are only making comments for week 1 programmers, it has very little value beyond then. "Is the pointer not NULL": yes that's what the code says, tell me what it means, such as "a newline was found in the text". "Finish when no longer true": okay you've just defined what a loop condition does; again, what does this mean?

So anyway, I had taken a stab at it earlier, but its a little overdone. I normally don't comment that well, although my editor's Javadoc support helps prod me into it a little more. In general, though, I try not to comment each line on that line -- but rather comment a group of lines that together are used to do something. Anyways, here it is.

Which editor do you use Dave ?

Agreed with Sanjay, comments should only be added when they are needed. Why? You're not trying to teach someone the C/C++ language -- and although you may have a difficult time initally understanding the code, someone who is experienced with C/C++ will just have a harder time distinguishing between "junk" comments and actually useful comments.

So, my advice is: if you feel like you're writing a comment which teaches someone about the language itself, stop. A general rule of thumb for me is that a comment's length should be proportional to the time it took to write the code that the comment covers. Also, you'll want to comment anything where your intention is difficult to see. You'll thank yourself later. ;)

Which editor do you use Dave ?

SlickEdit Version 11.0.2.

[edit]For example, hovering the cursor over the the function does this:

First, you should #include <string.h>. Second, ch needs to be an int. Also your function should return a value. And it is better to use sizeof(answer)/sizeof(*answer) instead of sizeof(answer)/sizeof(char).

Why to declare a int when a char which is less memory is all that I need?.

Why to return a pointer to a string when before it changed the string array?.
Could you help me to understand these.

Earlier I was trying to futz with the comments a little -- basically don't tell what the code is or is doing, but tell why.

Thank you for taking the time to correct and show a posible way of
commenting. That is great to see how real programmers do.
I can see that my comments look like 1st grader compared to yours.:sad:

Why to declare a int when a char which is less memory is all that I need?.

EOF is an int. getchar returns an int. http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1048865140&id=1043284351

Why to return a pointer to a string when before it changed the string array?.

You were returning an int. But you were not returning any value. Nor using it.

I just made it modestly more useful -- it could be chained, like this:
puts(read_string(answer, sizeof answer / sizeof *answer));

At worst, it still does the same as your original.

SlickEdit Version 11.0.2.

[edit]For example, hovering the cursor over the the function does this:

Hmm...it really is a good feature.

But any reason why you don't use the free Visual Studio Express Edition instead of paying for an expensive IDE. Just curious...

Only the world renowned modesty practiced by the inhabitants of the state of Minnesota must have prevented Dave from mentioning his many excellent articles on this very subject ...
http://www.daniweb.com/code/snippet597.html
http://www.daniweb.com/code/snippet353.html
http://www.daniweb.com/code/snippet597.html
http://www.daniweb.com/code/snippet357.html
http://www.daniweb.com/code/snippet358.html
http://www.daniweb.com/code/snippet373.html

I have used his wisdom in my work repeatedly.

EOF is an int. getchar returns an int.

You were returning an int. But you were not returning any value. Nor using it.

I just made it modestly more useful -- it could be chained, like this:
puts(read_string(answer, sizeof answer / sizeof *answer));

It makes sense to me now. :)
I just came to the conclusion that I need to check the reference for all
those functions I have learn to use through books, but I really don't know how to use correctly.

When Mr Dave Sinkula corrected the char variable for an int, I though:
"...If he says it, that's the way it must be. Which means I still don't know
even when to use a char data type". Now I see that I took for granted my understanding of the function getchar() and the EOF.

At worst, it still does the same as your original.

My thanks, truly.

If you don't mind to answer two questions:
Does every function have a hidden return(void) even if I don't use it nor write it in the code?.

When you have something like char *string(), Is there a way I could see were that pointer lives in memory?. Meaning the function. No the return.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.