How do I make sscanf ignore white spaces? C - c

I'm trying to read formatted data with sscanf in this format:
"%d,%d,%d"
for example,
sscanf(buffer, "%d,%d,%d", n1, n2, n3)
and it is critic that there is no white space between every number, because the input comes like this, for example:
109,304,249
194,204,482
etc.
But when I give sscanf an input like:
13, 56, 89
145, 646, 75
it still reads it even though it is not in the specifed format, and should be invalid
Is there is a way to write something such that sscanf will work as I expect it to work?

scanf ignores leading whitespace for most conversions, but what you want is the opposite of ignoring whitespace.
You cannot tell scanf to error out on a whitespace, but you can detect how many whitespace characters were consumed. For example:
#include <stdio.h>
int main()
{
int first, second, before, after;
int nread;
nread = scanf("%d,%n %n%d", &first, &before, &after, &second);
if (nread != 2) {
printf ("Error in the input stream, 2 items expected, %d matched\n", nread);
}
else if (before != after) {
printf ("Error in the input stream, %d whitespace characters detected\n",
after-before);
}
else {
printf ("Got numbers %d %d\n", first, second);
}
}
This detects whitespace before the second input.
Having said that, erroring out on whitespace is probably not a good idea.

The %d conversion format ignores leading white space as well as an optional + sign. If you want a stricter validation, you can:
use an extra validation phase before or after converting the values.
use a hand coded conversion that rejects malformed input.
Here are some propositions:
#include <stdio.h>
#include <string.h>
int main() {
char input[100];
char verif[100];
int a, b, c;
if (fgets(input, sizeof input, stdin)) {
if (sscanf(input, "%d,%d,%d", &a, &b, &c) == 3) {
snprintf(verif, sizeof verif, "%d,%d,%d\n", a, b, c);
if (strcmp(input, verif) == 0) {
printf("input is valid, a=%d, b=%d, c=%d\n", a, b, c);
return 0;
} else {
printf("input contains extra characters\n");
return 1;
}
} else {
printf("input does not match the pattern\n");
return 1;
}
} else {
printf("no input\n");
return 1;
}
}
You could just check if the input string contains any white space:
if (input[strcspn(input, " \t")] != '\0') {
printf("input string contains spaces or TABs\n");
}
But negative values, redundant + signs and leading zeroes will pass the test (eg: "-1,+1,+0001").
If all values must be positive, you could use scanf() to perform a poor man's pattern matching:
if (fgets(input, sizeof input, stdin)) {
char c1, c2;
if (sscanf(input, "%*[0-9],%*[0-9],%*[0-9]%c%c", &c1, &c2) == 1 && c1 == '\n') {
/* input line contains exactly 3 numbers separated by a `,` */
} else {
printf("invalid format\n");
}
}
Note however these remarks:
redundant leading zeroes would still be accepted by this validation phase (eg: "00,01,02").
overlong numbers will technically cause undefined behavior in all of the above methods (eg: "0,0,999999999999999999999999999999"), But the first approach will detect this problem if the conversion just truncates or maximises the converted value.
an overlong input line (longer than 98 bytes plus a newline) will be truncated by fgets(), leaving the rest of the input for the next read operation. This may be a problem too.
The solution to your problem depends on how strict and concise the validation must be.

One idea to validate the input is to use the regular expression to match three integers delimited by commas without including other characters.
#include <stdio.h>
#include <regex.h>
#include <string.h>
int main()
{
regex_t preg; // regex pattern
int ret;
int n1, n2, n3;
char *p; // pointer to a character th the string
char buffer[BUFSIZ]; // input buffer
// compile regex to match three integers delimited by commas
ret = regcomp(&preg, "^([+-]?[[:digit:]]+,){2}[+-]?[[:digit:]]+$", REG_EXTENDED);
if (ret) {
fprintf(stderr, "Compile error of regex\n");
exit(1);
}
while (fgets(buffer, BUFSIZ, stdin)) {
if ((p = rindex(buffer, '\n'))) *p = 0; // remove trailing newline
ret = regexec(&preg, buffer, 0, NULL, 0);
if (!ret) { // input matches the regex pattern
sscanf(buffer, "%d,%d,%d", &n1, &n2, &n3);
printf("=> %d,%d,%d\n", n1, n2, n3);
} else {
printf("Error in the input: %s\n", buffer);
}
}
return 0;
}
where ^([+-]?[[:digit:]]+,){2}[+-]?[[:digit:]]+$ is the regex:
^ anchors the start of the input string.
[+-]? matches zero or one leading plus or minus sign.
[[:digit:]]+, matches the sequence of digits followed by a comma.
The parentheses ( pattern ) makes a group of the pattern.
The quantifier {2} specifies the number of repetition of the previous atom (including group).
$ anchors the end of the input string.

Related

How to check if scanf("%s", &var) is a number, and thus turn it into an integer

I have this code and I need help converting the comments to c code
// if the input of scanf() is "q"
{
break;
}
else
{
// convert to int
}
Firstly, how do I check if an input is a certain character. Secondly, how do I turn a string into an integer. Example: "123" -> 123
Things I've tried, that didn't work: (it is possible that I implemented these solutions incorrectly)
how does scanf() check if the input is an integer or character?
Convert char to int in C and C++
I am not using any standard libraries except for stdio.h to print some logging information on the window
you have to know also that any string is terminated by null character which is '\0' to indicate the termination of the string , also you have to check is the user entered characters not numbers and so on (that's not implemented in this code).
I also handled if negative numbers are entered.
but you have to handle if the user entered decimals numbers , to sum up . there are so many cases to handle.
and here the edited code :
#include <stdio.h>
int main(){
char inputString[100];
printf("enter the input:\n");
scanf("%s", &inputString);
if(inputString[0] == 'q' && inputString[1] == '\0' )
{
printf("quiting\n");
//break;
}
else {
int i = 0;
int isNegative = 0;
int number = 0;
// check if the number is negative
if (inputString[0] == '-') {
isNegative = 1;
i = 1;
}
// convert to int
for ( ;inputString[i] != '\0' ; i++) {
number *= 10;
number += (inputString[i] - '0');
}
if(isNegative == 1)
number *= -1;
printf("you entered %d\n", number);
}
return 0;
}
The fundamental question here is, Do you want to use scanf?
scanf is everyone's favorite library function for easily reading in values. scanf has an input specifier, %d, for reading in integers.
And it has a different input specifier, %s, for reading in arbitrary strings.
But scanf does not have any single input specifier that means, "Read in an integer as an integer if the user types a valid integer, but if the user types something like "q", have a way so I can get my hands on that string instead."
Unless you want to move mountains and implement your own general-purpose input library from scratch, I think you have basically three options:
Use scanf with %d to read integers as integers, but check scanf's return value, and if scanf fails to read an integer, use that failure to terminate input.
Use scanf with %s to read the user's input as a string, so you can then explicitly test if it's a "q" or not. If not, convert it to an integer by hand. (More on this below.)
Don't use scanf at all. Use fgets to read the user's input as a whole line of text. Then see if it's a "q" or not. If not, convert it to an integer by hand.
Number 1 looks something like this:
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%d", &i) != 1) {
/* end of input detected */
break;
}
do something with i value just read;
}
The only problem with this solution is that it won't just stop if the user types "q", as your original problem statement stipulated. It will also stop if the user types "x", or "hello", or control-D, or anything else that's not a valid integer. But that's also a good thing, in that your loop won't get confused if the user types something unexpected, that's neither "q" nor a valid integer.
My point is that explicitly checking scanf's return value like this is an excellent idea, in any program that uses scanf. You should always check to see that scanf succeeded, and do something different if it fails.
Number 2 would look something like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%19s", tmpstr) != 1) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
This will work well enough, although since it uses atoi it will have certain problems if the user types something other than "q" or a valid integer. (More on this below.)
Number 3 might look like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(fgets(tmpstr, 20, stdin) == NULL) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q\n") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
One thing to note here is that fgets includes the newline that the user typed in the string it returns, so if the user types "q" followed by the Enter key, you'll get a string back of "q\n", not just "q". You can take care of that either by explicitly looking for the string "q\n", which is kind of lame (although it's what I've done here), or by stripping the newline back off.
Finally, for both #2 and #3, there's the question of, what's the right way to convert the user's string to an integer, and what if it wasn't a valid integer? The easiest way to make the conversion is to call atoi, as my examples so far have shown, but it has the problem that its behavior on invalid input is undefined. In practice, it will usually (a) ignore trailing nonnumeric input and (b) if there's no numeric input at all, return 0. (That is, it will read "123x" as 123, and "xyz" as 0.) But this behavior is not guaranteed, so these days, most experts recommend not using atoi.
The recommended alternative is strtol, which looks like this:
char *endp;
i = strtol(tmpstr, &endp, 10); /* convert string to integer */
Unlike atoi, strtol has guaranteed behavior on invalid input. Among other things, after it returns, it leaves your auxiliary pointer endp pointing at the first character in the string it didn't use, which is one way you can determine whether the input was fully valid or not. Unfortunately, properly dealing with all of the ways the input might be invalid (including trailing garbage, leading garbage, and numbers too big to convert) is a surprisingly complicated challenge, which I am not going to belabor this answer with.
Here are some guidelines:
scanf("%s", &var) is incorrect: you should pass the maximum number of characters to store into the array var and pass the array without the & as it will automatically convert to a pointer to its first element when passed as an argument:
char var[100];
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
to compare the string read to "q", you can use strcmp() declared in <string.h>:
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
to convert the string to the number it represents, use strtol() declared in <stdlib.h>:
char *p;
long value = strtol(var, &p, 0);
testing for a proper conversion is tricky: strtol() updated p to point to the character after the number and set errno in case of range error:
errno = 0;
char *p;
long value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
Here is a complete program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char var[100];
char *p;
long value;
printf("Enter a number: ");
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
errno = 0;
value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
}
You can try this: (Assuming only positive integers needs to convert)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Write C code here
char var[100];
int numb_flag=1, i=0,number=0;
scanf("%s",var);
while(var[i]!='\0') { // Checking if the input is number
if(var[i]>=48 && var[i]<=57)
i++;
else {
numb_flag = 0;
break;
}
}
if(numb_flag==1) {
number = atoi(var);
printf("\nNumber: %d",number);
} else {
printf("\nNot A Number");
}
return 0;
}
//Mind that in order to be more precise you could also use atof().
//The function works the same way as atoi() but is able to convert float
// (returned value is 'double') the string s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXILEN 100 /*change the value based on your needs*/
int main(){
int n, lim = MAXILEN;
char s[MAXILEN], *p = s, c;
/*taking the string from the input -- I do not use scanf*/
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
*p++ = c;
//here you can also do the check (if you want the '\n' char):
//if(c == '\n')
// *s++ = c;
*p = '\0';
if(s[0] == 'q' && s[1] == '\0')
exit(EXIT_SUCCESS); /*change the argument based on your needs*/
else
n = atoi(s);
printf("[DEBUG]: %d\n", n);
}

How to use set scanset in sscanf to read numbers like 0-9

How to read the input with scanset like[0-9] or [a-z] in sscanf function in C.We have not been able to successfully implement a scanset to scan through a string successfully.
This is my example:
void printResult(char * test, char * actual, char * expected)
{
printf("\n\n%d. %s\r",testcount++, test);
if(strcmp(actual,expected) == 0)
{
printf("SUCCESS\r");
printf("Output:%s", actual);
}
else
{
printf("FAILED\r");
printf("Expected Output:%s Actual Output:%s", expected, actual);
}
}
int main()
{
char buffer[] = "250MEL\r";
char * pBuffer = buffer;
char output[5]={0};
//1
sscanf(pBuffer,"%[ 0-9 ]s",output);
printResult("%[0-9]s", output, "250");
//2
sscanf(pBuffer,"%3[ 0-9 ]s",output);
printResult("%3[0-9]s", output, "250");
return 0;
}
can you anyone help me to use a scanset correctly.
TL;DR It isn't clear what makes you think your scan sets failed; they work OK in this context for me. The mildly modified code shown below demonstrates this.
As I noted in a comment, a scan set is of the form %[…] — it stops at the ] and anything following (s in the code in the question) is not part of the scan set. If you need to include ] in the scan set, it must be the first character (after the caret that negates a scan set, if you're using a negated scan set). With the s after the scan set, if the input includes an s after the end of the digit and blank sequence (the first blank in the scan set is significant; the second, being a repeat, is not), then that character will be 'consumed' and the next input operation would continue after the s; if the next character is not an s, then it is left in the input for the next input operation. Also, if the next character is not an s, the matching fails, but there is no way for sscanf() to report that when the scan set is the last or only conversion specification in the format string. Trailing context is always expendable; it's absence cannot be detected.
Your code is curious in that it uses \r in a number of places. You will seldom need to use \r in C code — you should use \n in your code (or blanks, or …).
Here's a program closely based on yours, with some changes. The code checks the return value from sscanf(); it replaces most of the carriage returns with some other character(s); it keeps the faulty format strings; it makes the argument to the print function match the argument to the sscanf() function.
#include <stdio.h>
#include <string.h>
static int testcount = 1;
static void printResult(char *test, char *actual, char *expected)
{
printf("\n\n%d. [%s]: ", testcount++, test);
if (strcmp(actual, expected) == 0)
{
printf("SUCCESS ");
printf("Output: [%s]\n", actual);
}
else
{
printf("FAILED ");
printf("Expected Output: [%s], Actual Output: [%s]\n", expected, actual);
}
}
int main(void)
{
char buffer[] = "250MEL\r";
char *pBuffer = buffer;
char output[5] = {0};
// 1
if (sscanf(pBuffer, "%[ 0-9 ]s", output) != 1)
printf("scanf() 1 failed\n");
printResult("%[ 0-9 ]s", output, "250");
// 2
if (sscanf(pBuffer, "%3[ 0-9 ]s", output) != 1)
printf("scanf() 1 failed\n");
printResult("%3[ 0-9 ]s", output, "250");
return 0;
}
It also produces the expected output:
1. [%[ 0-9 ]s]: SUCCESS Output: [250]
2. [%3[ 0-9 ]s]: SUCCESS Output: [250]
If you weren't seeing SUCCESS before, that's because the \r character moves the writing position to the start of the line, so what followed overwrote SUCCESS.
Auxilliary question
Also, please let me know how can I set a range for A-Z like below — but this is not working:
sscanf(pBuffer,"%*[A-Z]s",output);
printResult("%[A-Z]s",output, "MEL" );
Please pay attention!
Your comment is showing that you still think %[…]s is the notation for a scan set but the s is spurious; it is not part of the scan set notation. Stop thinking of %[…] as a modifier for %s; it isn't a modifier. It is a completely independent conversion specification, almost wholly unrelated to and syntactically quite distinct from %s. The square-bracket notation is also, categorically and unequivocally, not a part of any standard printf() conversion specification syntax.
Here is some revised code loosely based on the previous part of the answer (and hence on the code in the question). It isn't stellar, but it does show some useful information.
#include <stdio.h>
#include <string.h>
static int strings_match(const char *actual, const char *expected);
static void printResult(const char *format, const char *data, const char *act1,
char *exp1, const char *act2, char *exp2);
int main(void)
{
char buffer1[] = "250MEL#93";
char buffer2[] = " 250 \t\tMELabc";
char number[5] = "";
char letters[5] = "";
const char fmt1[] = "%4[0-9]%4[A-Z]";
const char fmt2[] = " %4[0-9] %4[A-Z]";
if (sscanf(buffer1, fmt1, number, letters) != 2)
printf("sscanf() 1 failed\n");
else
printResult(fmt1, buffer1, number, "250", letters, "MEL");
if (sscanf(buffer2, fmt2, number, letters) != 2)
printf("sscanf() 2 failed\n");
else
printResult(fmt2, buffer2, number, "250", letters, "MEL");
number[0] = '\0';
letters[0] = '\0';
if (sscanf(buffer2, fmt1, number, letters) != 2)
printf("sscanf() 3 failed\n");
else
printResult(fmt2, buffer1, number, "250", letters, "MEL");
const char fmt3[] = "%4[0-9]s%c";
const char fmt4[] = "%4[0-9]%c";
char buffer3[] = "9876sun";
char buffer4[] = "9876moon";
char letter;
if (sscanf(buffer3, fmt3, number, &letter) != 2)
printf("sscanf() 4 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer3, fmt3, number, letter);
if (sscanf(buffer3, fmt4, number, &letter) != 2)
printf("sscanf() 5 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer3, fmt4, number, letter);
if (sscanf(buffer4, fmt3, number, &letter) != 2)
printf("sscanf() 6 failed\n");
else
printf("Data [%s], Format [%s], Output [%s] %c\n", buffer4, fmt3, number, letter);
return 0;
}
static int strings_match(const char *actual, const char *expected)
{
int rc;
if (strcmp(actual, expected) == 0)
{
rc = 1;
printf(" Output: [%s]", actual);
}
else
{
rc = 0;
printf(" Expected Output: [%s], Actual Output: [%s]", expected, actual);
}
return rc;
}
static int testcount = 1;
static void printResult(const char *format, const char *data, const char *act1,
char *exp1, const char *act2, char *exp2)
{
printf("Format: %d. [%s] Data: [%s]", testcount++, format, data);
int t1 = strings_match(act1, exp1);
int t2 = strings_match(act2, exp2);
if (t1 == 1 && t2 == 1)
printf(" - SUCCESS\n");
else
printf(" - FAILED\n");
}
Output:
Format: 1. [%4[0-9]%4[A-Z]] Data: [250MEL#93] Output: [250] Output: [MEL] - SUCCESS
Format: 2. [ %4[0-9] %4[A-Z]] Data: [ 250 MELabc] Output: [250] Output: [MEL] - SUCCESS
sscanf() 3 failed
Data [9876sun], Format [%4[0-9]s%c], Output [9876] u
Data [9876sun], Format [%4[0-9]%c], Output [9876] s
sscanf() 6 failed
Note how the last two successful conversion lines differ — the s in the format string is matched as a literal to the data, leaving u to be read into the character, compared with when there is no s in the format string and the character matches the s. In contrast, when the format looks for an s and finds m, the overall sscanf() fails — it only manages 1 instead of 2 conversion specifications.

Using isdigit with if

int main()
{
int f;
printf("Type your age");
scanf("%d", &f);
if(!isdigit(f))
{
printf("Digit");
}
else
{
printf("Is not a digit");
}
return 0;
}
No matter if a typed 6 or a always shows me the "Digit" message
isdigit() should be passed a char not an int. And your if-else logic is reversed:
int main() {
char f;
printf("Type your age");
scanf("%c", &f);
if (isdigit(f)) {
printf("Digit");
} else {
printf("Is not a digit");
}
return 0;
}
As mentioned in the comments, this will only work for a single digit age. Validating input is a major topic under the 'C' tag, a search will reveal many approaches to more robust validation.
%d is an integer specifier. Change int f to char f and parse as a character. You are always passing an int into isdigit, which is why it is always true.
There's actually no need to use isdigit at all here since scanf with the %d format specifier already guarantees that the characters will be digits with an optional leading sign. And there's a separate specifier to get rid of the leading sign, %u.
If what you input isn't of the correct format, scanf will tell you (since it returns the number of items successfully scanned).
So, for a simple solution, you can just use something like:
unsigned int age;
if (scanf("%u", &age) == 1) {
puts("Not a valid age");
return 1;
}
// Now it's a valid uint, though you may want to catch large values.
If you want robust code, you may have to put in a little more effort than a one-liner scanf("%d") - it's fine for one-time or throw-away programs but it has serious shortcomings for code intended to be used in real systems.
First, I would use the excellent string input routine in this answer(a) - it pretty much provides everything you need for prompted and checked user input.
Once you have the input as a string, strtoul allows you to do the same type of conversion as scanf but with the ability to also ensure there's no trailing rubbish on the line as well. This answer (from the same author) provides the means for doing that.
Tying that all together, you can use something like:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
// Code to robustly get input from user.
#define OK 0 // Return codes - okay.
#define NO_INPUT 1 // - no input given.
#define TOO_LONG 2 // - input was too long.
static int getLine (
char *prmpt, // The prompt to use (NULL means no prompt).
char *buff, // The buffer to populate.
size_t sz // The size of the buffer.
) {
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;
}
// Code to check string is valid unsigned integer and within range.
// Returns true if it passed all checks, false otherwise.
static int validateStrAsUInt(
char *str, // String to evaluate.
unsigned int minVal, // Minimum allowed value.
unsigned int maxVal, // Maximum allowed value.
unsigned int *pResult // Address of item to take value.
) {
char *nextChar;
unsigned long retVal = strtoul (str, &nextChar, 10);
// Ensure we used the *whole* string and that it wasn't empty.
if ((nextChar == str) || (*nextChar != '\0'))
return 0;
// Ensure it's within range.
if ((retVal < minVal) || (retVal > maxVal))
return 0;
// It's okay, send it back to caller.
*pResult = retVal;
return 1;
}
// Code for testing above functions.
int main(void) {
int retCode;
unsigned int age;
char buff[20];
// Get it as string, detecting input errors.
retCode = getLine ("Enter your age> ", buff, sizeof(buff));
if (retCode == NO_INPUT) {
printf ("\nError, no input given.\n");
return 1;
}
if (retCode == TOO_LONG) {
printf ("Error, input too long [%s]\n", buff);
return 1;
}
// Check string is valid age.
if (! validateStrAsUInt(buff, 0, 150, &age)) {
printf("Not a valid age (0-150)\n");
return 1;
}
// It's okay, print and exit.
printf("Age is valid: %u\n", age);
return 0;
}
(a) I'm reliably informed the author is actually quite clever, and very good looking :-)

How to scanf one string, a pipe, and then another string all without any break/space in between?

The user will enter the configuration of the bus, like this
2
OO|XO
XX|XX
where
the number in the first line (let n) tells the number of rows, and
the following n lines show a pair of two seats
with a walkway in between denoted by |, a pipe.
A seat can either be occupied or empty. An empty seat is denoted by an O; an occupied one by an X.
char str1[3], str2[3];
I'm treating each pair of seats as a string,
char pipe;
and the pipe as a character.
I tried doing this:
scanf("%s %*c %s", str1, pipe, str2);
and this:
scanf("%s", str1);
pipe = getchar();
scanf("%s", str2);
but still no luck.
The question is: How to enter two/more characters/strings in one go without separating them with any kind of space or break?
scanf lets you specify any separator that you wish to use, including pipe. Since the pipe is always there, you don't need to scan it into your program, instead instructing scanf to skip it:
char a[3], b[3];
scanf("%2s|%2s", a, b);
printf("'%s' '%s'", a, b);
Demo.
First, read the property of %s conversion specifier. The input is delimited by a whitespace, not by a pipe |, at least not automatically.
The easiest workaround would be, to use a maximum field width, something like
scanf("%2s%*c%2s", str1, str2); // the * in %*<CS> indicates assignment suppression,
// you don't need a corresponding argument at all, and
// a wrong one will cause trouble
here, you don't need to modify the code if you wish to chose a different delimiter, it'll consider anything as a delimiter. If you want to enforce the use of |, you can write
scanf("%2s|%2s", str1, str2);
NOTE - never forget to check the return value of scanf() to ensure success.
There are 2 different ways to address your problem:
If the string length is fixed, you can just specify the maximum number of characters to read for %s and use this code:
char str1[3], str2[3];
if (scanf("%2s|%2s", str1, str2) == 2) {
/* read 2 characters into str1, a pipe and 1 or 2 characters into str2 */
}
If the string length is variable, such as on planes with different numbers of seats in some rows, you can use a scanset %[OX] and also specify the number of characters to read to prevent potential buffer overflow on unexpected input. Here is an example:
char str1[5], str2[5]; // handle up to 4 seats on each side */
if (scanf("%4[OX]|%4[OX]", str1, str2) == 2) {
/* read a group of 1 to 4 `X` or `O` into str1 and str2, separated by | */
}
You can add another conversion to further verify that the line has the expected format. Here is an example:
#include <stdio.h>
int main(void) {
char buf[128];
int i, n, c;
char left[3], right[3];
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "invalid format, empty file\n");
return 1;
}
if (sscanf(buf, "%d %c", &n, &c) != 1 || n < 0) {
fprintf(stderr, "invalid format, expected positive number: %s\n", buf);
return 1;
}
for (i = 0; i < n; i++) {
if (fgets(buf, sizeof buf, stdin) == NULL) {
fprintf(stderr, "missing %d lines\n", n - i);
return 1;
}
if (sscanf(buf, "%2[XO]|%2[XO] %c", left, right, &c) != 2) {
fprintf(stderr, "invalid format: %s\n", buf);
return 1;
} else {
printf("row %d: %s | %s\n", i + 1 + (i >= 12), left, right);
}
}
return 0;
}

C - reading ints and chars into arrays from a file

I have a .txt file with values written in this format: LetterNumber, LetterNumber, LetterNumber etc (example: A1, C8, R43, A298, B4). I want to read the letters and the numbers into two separate arrays (example: array1 would be A C R A B; array2 would be 1 8 43 298 4). How can I make it happen?
At the moment I only figured out how to read all the values, both numbers and letters and the commas and everything, into one array of chars:
FILE *myfile;
myfile = fopen("input1.txt", "r");
char input[677]; //I know there are 676 characters in my .txt file
int i;
if (myfile == NULL) {
printf("Error Reading File\n");
exit (0);
}
for (i=0; i<677; i++) {
fscanf(myfile, "%c", &input[i]);
}
fclose(myfile);
But ideally I want two arrays: one containing only letters and one containing only numbers. Is it even possible?
I would appreciate any kind of help, even just a hint. Thank you!
Define another array for integers,
int inputD[677];
Then in for loop read one char, one integer and one space char at a time.
fscanf(myfile, " %c%d %*[,] ", &input[i], &inputD[i]);
I would actually define a struct to keep letter and number together; the data format strongly suggests that they have a close relation. Here is a program that exemplifies the idea.
The scanf format is somewhat tricky to get right (meaning as simple as possible, but no simpler). RoadRunner, for example, forgot to skip whitespace preceding the letter in his answer.
It helps that we have (I assume) only single letters. It is helpful to remember that all standard formats except %c skip whitespace. (Both parts of that sentence should be remembered.)
#include<stdio.h>
#define ARRLEN 10000
// Keep pairs of data together in one struct.
struct CharIntPair
{
char letter;
int number;
};
// test data. various space configurations
// char *data = " A1, B22 , C333,D4,E5 ,F6, Z12345";
void printParsedPairs(struct CharIntPair pairs[], int count)
{
printf("%d pairs:\n", count);
for(int i = 0; i<count; i++)
{
printf("Pair %6d. Letter: %-2c, number: %11d\n", i, pairs[i].letter, pairs[i].number);
}
}
int main()
{
setbuf(stdout, NULL);
setbuf(stdin, NULL);
// For the parsing results
struct CharIntPair pairs[ARRLEN];
//char dummy [80];
int parsedPairCount = 0;
for(parsedPairCount=0; parsedPairCount<ARRLEN; parsedPairCount++)
{
// The format explained>
// -- " ": skips any optional whitespace
// -- "%c": reads the next single character
// -- "%d": expects and reads a number after optional whitespace
// (the %d format, like all standard formats except %c,
// skips whitespace).
// -- " ": reads and discards optional whitespace
// -- ",": expects, reads and discards a comma.
// The position after this scanf returns with 2 will be
// before optional whitespace and the next letter-number pair.
int numRead
= scanf(" %c%d ,",
&pairs[parsedPairCount].letter,
&pairs[parsedPairCount].number);
//printf("scanf returned %d\n", numRead);
//printf("dummy was ->%s<-\n", dummy);
if(numRead < 0) // IO error or, more likely, EOF. Inspect errno to tell.
{
printf("scanf returned %d\n", numRead);
break;
}
else if(numRead == 0)
{
printf("scanf returned %d\n", numRead);
printf("Data format problem: No character? How weird is that...\n");
break;
}
else if(numRead == 1)
{
printf("scanf returned %d\n", numRead);
printf("Data format problem: No number after first non-whitespace character ->%c<- (ASCII %d).\n",
pairs[parsedPairCount].letter, (int)pairs[parsedPairCount].letter);
break;
}
// It's 2; we have parsed a pair.
else
{
printf("Parsed pair %6d. Letter: %-2c, number: %11d\n", parsedPairCount,
pairs[parsedPairCount].letter, pairs[parsedPairCount].number);
}
}
printf("parsed pair count: %d\n", parsedPairCount);
printParsedPairs(pairs, parsedPairCount);
}
I was struggling a bit with my cygwin environment with bash and mintty on a Windows 8. The %c would sometimes encounter a newline (ASCII 10) which should be eaten by the preceding whitespace-eating space, derailing the parsing. (More robust parsing would, after an error, try to read char by char until the next comma is encountered, and try to recover from there.)
This happened when I typed Ctr-D (or, I think, also Ctr-Z in a console window) in an attempt to signal EOF; the following enter key stroke would cause a newline to "reach" the %c. Of course text I/O in a POSIX emulation on a Windows system is tricky; I must assume that somewhere between translating CR-NL sequences back and forth this bug slips in. On a linux system via ssh/putty it works as expected.
You basically just have to create one char array and one int array, then use fscanf to read the values from the file stream.
For simplicity, using a while loop in this case makes the job easier, as you can read the 2 values returned from fscanf until EOF.
Something like this is the right idea:
#include <stdio.h>
#include <stdlib.h>
// Wasn't really sure what the buffer size should be, it's up to you.
#define MAXSIZE 677
int
main(void) {
FILE *myFile;
char letters[MAXSIZE];
int numbers[MAXSIZE], count = 0, i;
myFile = fopen("input1.txt", "r");
if (myFile == NULL) {
fprintf(stderr, "%s\n", "Error reading file\n");
exit(EXIT_FAILURE);
}
while (fscanf(myFile, " %c%d ,", &letters[count], &numbers[count]) == 2) {
count++;
}
for (i = 0; i < count; i++) {
printf("%c%d ", letters[i], numbers[i]);
}
printf("\n");
fclose(myFile);
return 0;
}

Resources