Fundamental C - String I/O |
Written by Harry Fairhead | ||||||
Monday, 27 June 2022 | ||||||
Page 2 of 2
Low-level I/OThere are a number of I/O functions that are simpler than printf and scanf: putchar & getcharThese put and get a single char from the standard I/O streams. If you try: printf("type a character "); int c=getchar(); putchar(c); then the chances are very high that you will discover that buffers are still getting in the way and you don’t see the “type a character” message until after you have typed a character. The simplest fix is to use fflush, even if it is system-dependent: printf("type a character "); fflush(stdout); int c=getchar(); putchar(c); However, now you will discover that you have to press return after the character, once again because of the buffer. If you type “abcdef” then nothing happens till you press return, when the buffer is made available to getchar and a single character is removed from the buffer. You can use getchar again to read more of the buffer. Also notice that getchar and putchar work in terms of int rather than char. The two functions are sometimes useful, but not as a way to dynamically interact with the keyboard as you might expect. gets & putsThese two functions work like getchar and putchar but they work with complete C strings. For example: printf("type a string "); fflush(stdout); char s[25]; gets(s); puts(s); As in the case of getchar the buffer is only used by gets when the user presses return when gets reads characters into the string until it reaches the end of the buffer. The string s is null-terminated and includes any newline used to end the input. Notice that gets is dangerous in that it will accept as many characters as the user types and thus an array overflow is very possible. To avoid this problem use fgets instead. fgetsThe fgets function is designed to read a string from any data stream but it is the obvious alternative to the dangerous gets because it allows you to specify a maximum for the number of characters read. The safe equivalent of the previous example is: printf("type a string "); fflush(stdout); char s[25]; fgets(s,25,stdin); puts(s); Notice that the 25 in the call to fgets means you cannot have a buffer overflow but you can stop reading data before it is complete. As with reading a general array the solution to this is to repeat the read and process each chunk until all of the data has been processed. A Safe Way To Do Input – String ConversionThe scanf function is easy to use but both dangerous and unstable. Many C users when presented with this fact have in the past created their own version of scanf – a very complex alternative. A much better and simple way to proceed is to use fgets to safely read in a complete line of text and then use string conversion functions to extract the data. The string conversion functions are all of the same form: strtod(string, end); strtol(string, end, base); strtoul(string, end, base); which convert to double, long or unsigned long respectively. The string is scanned and the value built up as legal characters are encountered. The scanning stops when a character that cannot be part of a number is encountered or the end of the string. The end parameter is set to point at the location that the scan stopped so that the rest of the string can be processed. Finally, base is the numeric base to be used for the conversion, usually 10. For example: char s[]="1234.456 Some data"; char *prt; int num=strtol(s,&prt,10); printf("%d",num); Prints 1234 and leaves prt pointing at the space before “Some data”. For the moment don’t worry about the use of the & in &prt, it is explained in the next chapter. The strtol and strtoul work in the same way, converting legal characters to a value and stopping at the first non-legal character. You might wonder why there is no strtoi or similar? The simple answer is that there is no need as long can be reduced to int or short if the numeric value is small enough, and the same is true of unsigned long. There are some older functions atoi, atof and atol which convert a string to int, float and long respectively but don’t use them as they are can overrun the string. The atoi family of functions scan a string until they find a suitable set of characters to convert. That is: atoi(“the number is 123”); will return 123 whereas strtol stops at once on ‘t’. You can use the fact that ptr is the start of the string to test to see if any valid characters were found. The problem is that atoi will carry on scanning a string until it finds a valid character even if this results in it going beyond the end of the string. The best way to do safe input from the keyboard is to use the strto functions on the string returned from fgets. For example, suppose you want the user to input an integer and a number with a decimal point separated by a comma: char myString[25]; printf("type a int,double "); fflush(stdout); fgets(myString,25,stdin); char *prt; int num1=strtol(myString,&prt,10); printf("%d",num1); prt++; double num2=strtod(prt,&prt); printf("%f",num2); The fgets reads in a whole line from the user and it allows the user to edit the line before pressing enter. Next we use strtol to extract the integer digits. The scan stops at the comma and this is what prt is pointing at. Adding one to prt moves it past the comma, in a real application we need to check that the comma is there and that the floating value is next. The strtod extracts the floating value. All of this is easy and safe and, if you need to get input from the keyboard, is the best approach unless you are using a library or GUI framework. Summary
Related ArticlesGetting Started With C/C++ On The Micro:bit Fundamental C: Getting Closer To The MachineNow available as a paperback and ebook from Amazon.
Also see the companion volume: Applying C <ASIN:1871962609> <ASIN:1871962463> <ASIN:1871962617> <ASIN:1871962455>
To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.
Comments
or email your comment to: comments@i-programmer.info |
||||||
Last Updated ( Monday, 27 June 2022 ) |