Read data to Text File and reserve de output with N character - c

I want read data from console and output to Text file with reserve of N character per variable of structure type.
The Text file is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
I do not know if I'm using the most correct functions.
Also I can not read ("carro.name") more than 1 word (example: John Lays)
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
...
//Read data to Text File:
...
printf("\n ID......:"); scanf("%d", &carro.id);
printf("\n Potencia:"); scanf("%d", &carro.potencia);
printf("\n Avariado:"); scanf("%d", &carro.avariado);
printf("\n NAME:"); scanf("%10[0-9a-zA-Z ]", carro.name); // or scanf("%[^\n]s",...)
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
...
//Show File Text data:
...
int registos=0;
while(1)
{
fscanf(fp, "%d %d %d %-10s", &carro.id, &carro.potencia, &carro.avariado, carro.name);
if(feof(fp)){ break; }
printf("%-2d %-3d %-1d %-10s\n", carro.id, carro.potencia, carro.avariado, carro.name);
registos++;
}
printf("\nCarros=%d", registos);

As you say in your question you cannot use scanf to read a complex name including spaces.
But before to search how to do it is needed to decide what to do.
Probably you do not want to memorize the extra spaces at the beginning and at the end (including the newline), and probably a name must not be empty.
But what about inside a complex name ? If the user enter John Lays do you save the name with the two spaces or you want to simplify to have only one ? Do you have to manage other special character like '-' (are John - Lays / John- Lays / John -Lays read as John-Lays ?).
What to do if the input string is longer than 10 characters ? Just to stop to read letting the rest for the next read or to bypass up to a newline ? Because you print a message before each input you clearly want an input per line and the rest of the line must be bypassed.
If you do not want to read the string as it is enter the best way is probably to write your own read string function.
You also have to decide what to do if the user do not enter a number for ID or Potencia or Avariado, currently you do not even detect the error, this is not a good way. So in that case do you abort all (exit program), or you redo the read ? Probably you prefer to read again, for that you need to bypass the invalid input, but what that means, to bypass all up to a newline ?
For instance :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* read an int memorizing its value in v,
return 0 in case of EOF else a non null value */
int readInt(const char * msg, int * v)
{
for (;;) {
fputs(msg, stdout);
if (scanf("%d", v) == 1)
return 1;
/* not a number or EOF, bypass all up to \n */
int c;
while ((c = fgetc(stdin)) != '\n')
if (c == EOF)
return 0;
puts("invalid value"); /* message may be also get in argument */
}
}
/* read a string up to a \n
remove extra spaces at the beginning and end
simplify internal multiple spaces
accept any character and do not manage in a special way characters like like '-'
a non empty string must be read
read at most sz-1 characters in s then place the null character (as fgets), sz must be > 1
if the line too long bypass the rest of the input up to \n
return 0 in case of EOF else a non null value */
int readStr(const char * msg, char * s, size_t sz)
{
fputs(msg, stdout);
/* read the first char bypassing spaces including \n */
if (scanf(" %c", s) == 0)
// EOF
return 0;
size_t index = 1;
int c;
sz -= 1;
while (index != sz) {
c = fgetc(stdin);
if ((c == EOF) || (c == '\n'))
break;
if (!isspace(c))
s[index++] = c;
else if (s[index - 1] != ' ')
s[index++] = ' ';
}
s[(s[index - 1] != ' ') ? index : index-1] = 0;
// bypass possible rest of the line
while ((c != EOF) && (c != '\n'))
c = fgetc(stdin);
return 1;
}
/* ******************* */
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
int main()
{
do {
if (!readInt("\n ID......:", &carro.id) ||
!readInt("\n Potencia:", &carro.potencia) ||
!readInt("\n Avariado:", &carro.avariado) ||
!readStr("\n NAME:", carro.name, sizeof(carro.name))) {
puts("EOF");
return -1;
}
else
printf("%-2d %-3d %-1d '%-10s' \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
} while (strcmp(carro.name, "end"));
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall r.c
pi#raspberrypi:/tmp $ ./a.out
ID......:aze
invalid value
ID......:qsd
invalid value
ID......:1
Potencia:2
Avariado:3
NAME:aze u iiiiiiiiiiiiiiiiii
1 2 3 'aze u iiii'
ID......:11
Potencia:22
Avariado:0
NAME: end
11 22 0 'end '
pi#raspberrypi:/tmp $
When you read in your file and supposing it was produced doing fprintf(fp, "%-2d %-3d %-1d %-10s", ...) :
char line[21]; /* each line has 20 characters newline included */
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%d %d %d", &carro.id, &carro.potencia, &carro.avariado) != 3)
/* abnormal case, invalid file */
break; /* anything else you want to do */
/* the string starts at the index 9 and has 10 characters out of the newline */
memcpy(carro.name, line + 9, 10);
carro.name[10] = 0;
/* ... */
}
note the name have spaces at the end if its length is less than 10 characters
Or you can read in a way similar to the previous on stdin.

Related

Check new lines before format element scanf

So, I want to read some input in two integer variables with fscanf, the input must be
INTEGER ONE SPACE INTEGER \n
So for example a file could be like that:
235 190\n
And what I want to do is checking if there is any new line before the first integer or the second, then throw an exception if that's the case:
So basically what you do with fscanf for that case would be just to do like:
FILE *somefile;
[...]
int w, h;
if (fscanf(somefile, "%d%d") == EOF)
return (1);
But this method does not provide checking if there is a new line between the integers
(for example if there is INTEGER NEW_LINE INTEGER it will still read the second)
as it skips all spaces before seeing a digit, so what I managed to do is more close but still wrong:
FILE *somefile;
int w, h;
char check[2];
if (fscanf(somefile, "%d%c%d%c", &w, check, &h, check + 1) == EOF)
return (-1);
if (check[0] != ' ' || check[1] != '\n')
return (-1);
So after I did that I realized that the %c before the %d just reads one character, so if there is a space then a new line between the two, it will continue reading the file wrongly and won't detect an error.
Now I wonder if there is a way to skip all spaces like when using " %c", while knowing if we skipped a \n. Thanks for your help.
PS: I know we can use fgets and strtol but it would be too easy, in this project I can only use fscanf to parse the file.
scanf happily discards leading whitespace to match a %d conversion specifier, which makes this slightly difficult to do. But you can certainly use fgetc (or getchar) to validate a character and then push it back onto the stream for scanf to consume. Something like:
$ cat a.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
int c;
int d[2];
c = getchar();
if( 0
||!( c == '+' || c == '-' || isdigit(c))
|| ( c != ungetc(c, stdin) )
|| ( scanf("%d", d) != 1 )
|| ( getchar() != ' ' )
|| ( (c = getchar()) == EOF )
||!( c == '+' || c == '-' || isdigit(c))
|| ( c != ungetc(c, stdin) )
|| ( scanf("%d", d + 1) != 1 )
|| ( getchar() != '\n' )
){
fprintf(stderr, "invalid input!\n");
return EXIT_FAILURE;
}
printf("Read %d %d\n", d[0], d[1]);
return EXIT_SUCCESS;
}
$ gcc a.c
$ printf '5 28\n' | ./a.out
Read 5 28
$ printf '5 28\n' | ./a.out
invalid input!
$ printf ' 5 28\n' | ./a.out
invalid input!
$ printf ' 5 28' | ./a.out
invalid input!
if there is a way to skip all spaces
I can only use fscanf to parse the file.
Let us assume ungetc() is not allowed - which makes things harder.
"%d" happily consumes all (0 or more) leading white-spaces including '\n' without notice.
Use fscanf("%[...]") to find leading white-spaces first. The usual suspects are " \t\n\r\f\v".
// Consume leading whitespaces
// Return
// 1: \n or EOF found
// 0: not found
int EatLeadingWS(FILE *f) {
int count;
char space[2];
while ((count = fscanf("%1[ \t\n\r\f\v]", space)) == 1) {
if (space[0] == '\n') return 1;
}
return count == EOF;
}
Alternative: use "%c" and isspace(), but then we need a way to put non white-spaces back.
Then look for white-spaces before "%d"
int some_int;
if (EatLeadingWS(f) == 0) {
int count;
if ((count = fscanf("%d", &some_int)) == 1) {
printf("int %d found before a \\n\n", some_int);
}
}
Another alternative is to use char buf[100]; fscanf("%99[^\n]", buf) to read most of the line and then parse the string with sscanf(buf, "%d%1[ ]%d" ....

How to find the length of every word in a string?

I wrote a code that takes a sentence and outputs every each word in a line. But I also want to write the size of each word next to it.
Input:
Hi my name is
Current output:
Hi
my
name
is
Desired output:
Hi(2)
my(2)
name(4)
is(2)
My current Code:
#include <stdio.h>
#define MAX 100
int main(void) {
int c = 0;
size_t n = 0;
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ')
printf("\n");
else
printf("%c", c);
n++;
}
printf("\n");
if (n == MAX) /* read and discard remaining chars in stdin */
while ((c = getchar()) != '\n' && c != EOF);
return 0;
}
How can I do that?
For completeness a different approach reading the whole input in one call and then tokenising it:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
char s[MAX +1];
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
if (NULL == fgets(s, sizeof s, stdin))
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
s[strcspn(s, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (char * pc = strtok(s, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
return result;
}
As the read buffer is never explicitly used the following variation is possible as well:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
printf("\n Enter a sentence.\n\n input: ");
{
/* read up to 100 characters from stdin, print each word on a line */
char * pc = fgets((char[MAX+1]), MAX+1, stdin);
if (NULL == pc)
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
pc[strcspn(pc, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (pc = strtok(pc, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
}
return result;
}
Have one more variable and print out when you hit space.
size_t len = 0;
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ') {
printf("(%u)\n", len);
len = 0;
}
else {
len++;
printf("%c", c);
}
n++;
}
In addition to the good answer by #kiranBiradar, you may want to add an additional variable that allows you to track whether you are in-a-word reading characters or outside-a-word reading whitespace. (using a simple int value as a flag set to 1 (true) for in-word or 0 (false) not-in-word is all you need) This will allow you to ignore leading whitespace, multiple included whitespace, or trailing whitespace in your input, e.g., if your input was similar to:
" my dog has fleas and my cat has none "
Unless you are keeping track of the state of your read, whether you are in-word / not-in-word, you will be outputting multiple occurrences of "(0)\n" each time a whitespace character is read. By keeping a flag of whether you are in / not-in a word and setting it zero when you encounter your first whitespace after being in-word reading non-whitespace characters allows you to only output the length once on the first whitespace encountered.
Additionally, conditioning your read on c != '\n' will skip outputting the length of the final word, unless you include additional code after you exit your read loop.
Also by including <ctype.h> you have the isspace() macro available to check for all whitespace (e.g, space, tab, newline, backspace, vertical-tab, etc...) It greatly simplifies your conditional checks.
Putting it altogether you could do:
#include <stdio.h>
#include <ctype.h> /* for isspace() */
int main (void) {
int c = 0, in = 0, len = 0; /* char, in/out flag, length */
fputs ("enter text: ", stdout); /* prompt for text */
fflush (stdout); /* (optional), but recommended */
while ((c = getchar()) != EOF) { /* loop reading chars until EOF */
if (isspace (c)) { /* if input is space */
if (in) { /* check if in-word */
printf ("(%d)\n", len); /* output (len) */
len = 0; /* reset len zero */
in = 0; /* set in flag zero (false) */
}
if (c == '\n') /* if space is \n */
break; /* break read loop */
}
else { /* if not whitespace */
putchar (c); /* output char */
len++; /* increment length */
in = 1; /* set in flag 1 (true) */
}
}
}
(note: there is no reason to limit your read to n < MAX unless you simply want to arbitrarily limit your read of characters to the first 100-characters. There is no array being filled or other storage being occupied by the character c (aside from it's one byte). You could read billions if your input contained them)
Example Use/Output
$ ./bin/getchar_word_len
enter text: my dog has fleas and my cat has none
my(2)
dog(3)
has(3)
fleas(5)
and(3)
my(2)
cat(3)
has(3)
none(4)
Look over both answers and let either of us know if you have further questions. If you are confused by the logic, take out an 8.5x11 sheet of paper and work through the logic of the loop for each character starting at the beginning of your input. It will make sense by the time you work through the first word.

"How to make my program read visible characters and spaces from a file

I am supposed to be "fixing" code given to me to make it display the correct number of visible characters in a file (spaces too). The correct number is supposed to be 977. I have never dealt with files before and I don't understand what I need to do to display the correct number.
* Driver Menu System for Homework
* Andrew Potter - Mar 5, 2019 <-- Please put your name/date here
*/
#include <stdio.h>//header file for input/output -
#include <stdlib.h>
#include <ctype.h>
// since you will place all your assigned functions (programs) in this file, you do not need to include stdio.h again!
int menu(void); //prototype definition section
void hello(void);
void countall(void);
int main(void)
{
int selection = menu();
while(selection != 99) {
switch(selection) {
case 1:
hello();
break;
case 2:
countall();
break;
case 3:
break;
case 4:
break;
default:
printf("Please enter a valid selection.\n");
}
selection = menu();
}
return 0;
}
int menu(void) {
int choice;
printf("***************************\n");
printf(" 1. Hello \n");
printf(" 2. Countall\n");
printf(" 3. \n");
printf(" 4. \n");
printf("99. Exit\n");
printf("Please select number and press enter:\n");
printf("***************************\n");
scanf("%d", &choice);
getchar();
return choice;
}
void hello(void) {
printf("Hello, World!!!\n");
}
//*****Andrew 5/1/19*****
#define SLEN 81 /* from reverse.c */
/* original header: int count(argc, *argv[]) */
void countall(void)
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
long unsigned count = 0;
char file[SLEN]; /* from reverse.c */
/*Checks whether a file name was included when run from the command prompt
* The argument count includes the program file name. A count of 2 indicates
* that an additional parameter was passed
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
* The following uses the second parameter as the file name
* and attempts to open the file
if ((fp = fopen(argv[1], "r")) == NULL)
{
printf("Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
} */
/*************************************
Code from reverse.c included to make the program work from within our IDE
*************************************/
puts("Enter the name of the file to be processed:");
scanf("%s", file);
if ((fp = fopen(file,"rb")) == NULL) /* read mode */
{
printf("count program can't open %s\n", file);
exit(EXIT_FAILURE);
}
/* EOF reached when C realizes it tried to reach beyond the end of the file! */
/* This is good design - see page 573 */
while ((ch = getc(fp)) != EOF)
{
if (isprint(ch)) {
count++;
}
else if (isprint(ch)) {
count++;
}
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("\nFile %s has %lu characters\n", file, count);
}
I expected I would get the correct number of visible characters using the combination of isprint and isspace but I usually get 2086.
The assignment directions are: "Word identifies 977 characters including spaces. Your current countall() believes there are 1043. Make the corrections necessary to your code to count only the visible characters and spaces! (Hint: check out 567 in your textbook.)" Before I edited any code the count was 1043, now i am getting 2020. I need 977.
isprint() returns a Boolean result - zero if the character is not "printable", and non-zero if it is. As such isprint(ch) != '\n'makes no sense. Your complete expression in the question makes even less sense, but I'll come on to that at the end.
isprint() on its own returns true (non-zero) for all printable characters, so you need no other tests. Moreover you increment count unconditionally and in every conditional block, so you are counting every character and some twice.
You just need:
if( isprint(ch) )
{
count++;
}
putc( ch, stdout ) ;
While your code is clearly an incomplete fragment, it is not clear where or how your are reading ch. You need a getc() or equivalent in there somewhare.
while( (ch = getc(fp)) != EOF )
{
if( isprint(ch) )
{
count++;
}
putc( ch, stdout ) ;
}
It is not clear whether you need to count all whitespace (including space, tab and newline) or just "spaces" as you stated. If so be clear that isprint() will match space, but not control characters newline or tab. isspace() matches all these, but should not be counted separately to isprint() because 'space' is in both white-space and printable sets. If newline and tab are to be counted (and less likely; "vertical tab") then:
while( (ch = getc(fp)) != EOF )
{
if( isprint(ch) || isspace(ch) )
{
count++;
}
putc( ch, stdout ) ;
}
Another aspect of C that you seem to misunderstand is how Boolean expressions work. To test a single variable for multiple values you must write:
if( var == x || var == y || var == z )
You have written:
if( var == x || y || z )
which may make sense in English (or other natural language) when you read it out aloud, but in C it means:
if( var == (x || y || z ) )
evaluating (x || y || z ) as either true or false and comparing it to var.
It is probably worth considering the semantics of your existing solution to show why it actually compiles, but produces the erroneous result it does.
Firstly,
isprint(ch) != '\n' || '\t' || '\0'
is equivalent to isprint(ch) != true, for the reasons described earlier. So you increment the counter for all characters that are not printable.
Then here:
isspace(ch) == NULL
NULL is a macro representing an invalid pointer, and isspace() does not return a pointer. However NULL will implicitly cast to zero (or false). So here you increment the counter for all printable characters that are not spaces.
Finally, you unconditionally count every character here:
putc(ch,stdout); // same as putchar(ch);
count++;
So your result will be:
number-of-non-printing-characters +
number-of-printing-characters - number-of-spaces +
total-number-of-characters
which is I think (2 x file-length) - number-of-spaces
Finally note that if you open a text file that has CR+LF line ends (conventional for text files on Windows) in "binary" mode, isspace() will count two characters for every new-line. Be sure to open in "text" mode (regardless of the platform).
From isprint():
A printable character is a character that occupies a printing position on a display (this is the opposite of a control character, checked with iscntrl).
and
A value different from zero (i.e., true) if indeed c is a printable character. Zero (i.e., false) otherwise.
So that function should be sufficient. Please note that you have to make sure to feed all these is...() functions from <ctype.h> unsigned values. So if you use it with a value of uncertain origin, better cast to char unsigned.
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
int main(void)
{
char const *filename = "test.txt";
FILE *input = fopen(filename, "r");
if (!input) {
fprintf(stderr, "Couldn't open \"%s\" for reading. :(\n\n", filename);
return EXIT_FAILURE;
}
long long unsigned count = 0;
for (int ch; (ch = fgetc(input)) != EOF;) {
if (isprint(ch))
++count;
}
fclose(input);
printf("Count: %llu\n\n", count);
}
If I wasn't lucky enough to guess which characters you want to be counted, have a look at ctype.h, there is a table.
if ((ch == '\t') || isprint(ch))
count++;
If you want to handle tabs differently (maybe to count how many spaces they use):
if (ch == '\t') {
/* Do smth */
} else if (isprint(ch)) {
count++;
}
This should be enough.

Detect too long string at scanf() by given scanset

Scenario:
I am trying to validate some user-input.
In my case the user is only allowed to
enter between 0 and 3 lowercase characters,
optionally including whitespace.
In Regex: ^[a-z ]{0,3}$
If the user enters more than 3 characters or
the input string contains invalid values,
in each case a different return-value and
error message has to be printed.
What I tried is to read the input into a temporary
char array and defining the scanset as 4[a-z ],
so that only the correct characters will be read
and one char more, in order to check if the maximal
number of desired characters has been read.
I.e. if the last element in this temporary array
is not empty the user input was bigger than 3.
Problem:
When the user enters 3 correct chars
and a 4th wrong char, the 4th won't be read,
therefore we read 3 valid chars,
we "allegedly" never read an invalid char and
the length of read chars is also valid,
all tough it of course is not!
Code:
//--------------------------------------
int scan_input(char* char_array)
{
int status = 0;
int max_size = 3;
char temp_array[max_size+1];
// Print system prompt:
printf("plain text: ");
// Read user input:
status = scanf("%4[a-z ]", temp_array);
if (temp_array[max_size] != '\0')
{
printf("[ERR] too many characters\n");
return -1;
}
if (status != 1)
{
printf("[ERR] invalid characters\n");
return -2;
}
strcpy(char_array,temp_array);
printf("[OK] Input is valid!\n");
return 0;
}
Output:
$ gcc -Wall -std=c11 application.c && ./a.out
plain text: abcD
[OK] Input is valid!
I am grateful for every hint to fix this blind spot!
PS.:
If you know a better approach to solve this problem, than by doing it with scanf() and the scanset, your thoughts are welcome!
to validate some user-input
Separate the input from the parsing
Use a wide buffer and fgets().
char buf[80];
if (fgets(buf, sizeof buf, stdin)) {
// we have some input
Then parse and use "%n", which records the scan position, to test success.
int max_size = 3;
char temp_array[max_size+1];
int n = 0;
temp_array[0] = '\0';
sscanf(buf, "%3[a-z ]%n", temp_array, &n);
bool success = buf[n] == '\n' || buf[n] == '\0';
If sscanf() did not scan anything, n == 0 and the prior temp_array[0] = 0 insures a null character.
If the scan succeeded, n > 0 and code inspects the next character.
Alternative staying with scanf()
status = scanf("%3[a-z ]", temp_array);
// When nothing read, form "" string
if (status != 1) {
temp_array[0] = '\0';
}
bool success = true;
if (status == EOF) {
success = false;
} else {
// consume rest of line, noting if extra junk followed
int next_ch;
while ((next_ch = fgetc(stdin)) != '\n' && next_ch != EOF) {
success = false; //Extra junk
}
}

Get scanf to quit when it reads a newline?

If I input 5 5 at the terminal, press enter, and press enter again, I want to exit out of the loop.
int readCoefficents(double complex *c){
int i = 0;
double real;
double img;
while(scanf("%f %f", &real, &img) == 2)
c[i++] = real + img * I;
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;
}
Obviously, that code isn't doing the job for me (and yes, I know there is a buffer overflow waiting to happen).
scanf won't quit unless I type in a letter (or some non-numeric, not whitespace string). How do I get scanf to quit after reading an empty line?
Use fgets to read console input:
int res = 2;
while (res == 2) {
char buf[100];
fgets(buf, sizeof(buf), stdin);
res = sscanf(buf, "%f %f", &real, &img);
if (res == 2)
c[i++] = real + img * I;
}
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;
The specific problem you're having is that a scanf format string of %f will skip white space (including newlines) until it finds an actual character to scan. From the c99 standard:
A conversion specification is executed in the following steps:
- Input white-space characters (as specified by the isspace function) are skipped, unless the specification includes a '[', 'c', or 'n' specifier.
and, elsewhere, describing isspace():
The standard white-space characters are the following: space ' ', form feed '\f', new-line '\n', carriage return '\r', horizontal tab '\t', and vertical tab '\v'.
Your best bet is to use fgets to get the line (and this can be protected from buffer overflow very easily), then use sscanf on the resultant line.
The scanf function is one of those ones you should look at very warily. The following piece of code is one I often use to handle line input:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
Testing it with various combinations:
pax> ./prog
Enter string>[CTRL-D]
No input
pax> ./prog
Enter string> a
OK [a]
pax> ./prog
Enter string> hello
OK [hello]
pax> ./prog
Enter string> hello there
Input too long [hello the]
pax> ./prog
Enter string> i am pax
OK [i am pax]
What I would do is to use this function to get a line safely, then simply use:
sscanf (buffer, "%f %f", &real, &img)
to get the actual values (and check the count).
In fact, here's a complete program which is closer to what you want:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
int main (void) {
int i = 1, rc;
char prompt[50], buff[50];
float real, imag;
while (1) {
sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i);
rc = getLine (prompt, buff, sizeof(buff));
if (rc == NO_INPUT) break;
if (*buff == '\0') break;
if (rc == TOO_LONG) {
printf ("** Input too long [%s]...\n", buff);
}
if (sscanf (buff, "%f %f", &real, &imag) == 2) {
printf ("Values were %f and %f\n", real, imag);
i++;
} else {
printf ("** Invalid input [%s]\n", buff);
}
}
return 0;
}
along with a test run:
pax> ./testprog
Enter real and imaginary for # 1: hello
** Invalid input [hello]
Enter real and imaginary for # 1: hello there
** Invalid input [hello there]
Enter real and imaginary for # 1: 1
** Invalid input [1]
Enter real and imaginary for # 1: 1.23 4.56
Values were 1.230000 and 4.560000
Enter real and imaginary for # 2:
pax> _
There's a way to do what you want using just scanf:
int readCoefficents(double complex *c) {
int i = 0;
double real;
double img;
char buf[2];
while (scanf("%1[\n]", buf) == 0) { // loop until a blank line or EOF
if (scanf("%lf %lf", &real, &img) == 2) // read two floats
c[i++] = real + img * I;
scanf("%*[^\n]"); // skip the rest of the line
scanf("%*1[\n]"); // and the newline
}
c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1
return i;
}
If the user only enters 1 float on a line, it will read the next line for the second value. If any random garbage is entered, it will skip up to a newline and try again with the next line. Otherwise, it will just go on reading pairs of float values until the user enters a blank line or an EOF is reached.
re PAXDIABLO solution: it does not work properly with EMPTY line entered by user, so this line shall be added in your getLine() function
if (strlen(buff) <= 1) return NO_INPUT;
after the line:
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
So it will become :
...
if (strlen(buff) <= 1) return NO_INPUT;
if (fgets (buff, sz, stdin) == NULL) return NO_INPUT;
....
Instead of
while(scanf("%f %f", &real, &img) == 2)
try
while(scanf("%f %f%*c", &real, &img) == 2)
scanf("%f%*c", &myfloat); // will read a float and all eventual characters after it

Resources