Is there something like a wildcard character for sscanf() - c

My string may or may not have some parameters, and so I want to insert that flexibility into my sscanf.
Eg My string can be "111|g|8|9|r|4|5" , Where the 3rd and 4th are optional paramaters, so we can have an input like this "111|g|||r|4|5"
Is there something like a wildcard character like * in regex which I can use ?
eg sscanf(mystr,"%d|%c|*|*|%c|%d|%d", &int1, &char1, &int2, &int3, &char2,&int4,&int5);

No you cannot.
scanf can only do simple conversions. If you need more complex matching, you must build a custom parser or use a richer library.
Here, the parsing can be done by hand:
store the current position in string
search the next '|' in the string with strchr => if not found, you have last token
store its position + 1 as a hold position
replace the '|' with a '\0'
the current token is the string starting at current position
set the current position to the hold position and iterate

I don't think you can be that selective with scanf so I suggest you use fgets and then sort the returned data.

No, not in the way you want to use it. However, there are alternatives.
As others have stated, you could use strsep() to separate the input.
The following code allows any of the fields to be empty, which in essence, is a more like a format code or wildcard (explained in regex as \d|*\|\w|*\|\d|*\|\d|*\|\w|*\|\d|*\|\d|*)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *my_string;
my_string = malloc(sizeof(char)*256);
char *my_string_split;
printf("Input: ");
fgets(my_string, 256, stdin);
char char1;
char char2;
int int1;
int int2;
int int3;
int int4;
int int5;
my_string_split = strsep(&my_string, "|");
for (int i = 0; my_string_split != NULL; i++) {
switch (i) {
case 0:
sscanf(my_string_split, "%d", &int1);
break;
case 1:
sscanf(my_string_split, "%c", &char1);
break;
case 2:
sscanf(my_string_split, "%d", &int2);
break;
case 3:
sscanf(my_string_split, "%d", &int3);
break;
case 4:
sscanf(my_string_split, "%c", &char2);
break;
case 5:
sscanf(my_string_split, "%d", &int4);
break;
case 6:
sscanf(my_string_split, "%d", &int5);
break;
}
my_string_split = strsep(&my_string, "|");
}
printf("%d|%c|%d|%d|%c|%d|%d", int1, char1, int2, int3, char2, int4, int5);
free(my_string);
return 0;
}
Normal input
Input: 111|g|8|9|r|4|5
111|g|8|9|r|4|5
Handles input with empty fields
Input: 111|g|||r|4|5
111|g|0|0|r|4|5
Also, if your system doesn't provide strsep() there's another stackoverflow question which has a solution to this: https://stackoverflow.com/a/8514474/6051408

Related

C language - Menu issue with multiple void statements and do-switch-case

I am trying to make a menu with the options to compress a text inputted by the user and then store that value to be extracted in the extract menu option.
The issue lies in that it seems like the code isn't following the void statements, for example
case 1: compress();//compress statement
It seems to only get the printf statement in the void compress(void) and not the scanf, which it then follows with the loop of the menu.
Any solutions?
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
void menu(void);
void compress(void);
void extract(void);
int main(void)
{
menu();
return 0;
}
void menu(void)
{
int choice;
do
{
printf("Menu\n\n");
printf("1. Compress Text\n");
printf("2. Extract Text\n");
printf("3. Exit\n");
scanf_s("%d", &choice);
switch (choice)
{
case 1: compress();//compress statement
break;
case 2: extract();//extract statement
break;
case 3: printf("Ciao\n");
exit(0);
break;
default: printf("Invalid Entry. Try again\n");
break;
}
} while (choice != 3);
}
void compress(void) {
printf("\n-------------------------\n");
printf(" COMPRESS ");
printf("\n-------------------------\n");
printf("\nPlease enter a word/sentence to be compressed:\n");
char txt[200];
scanf_s("%c", &txt);
printf("\nYour word/sentence is %c", txt, "\n");
char comp = strlen(txt);
int mask[200]{};
for (int i = 0; i < comp; ++i) //loop until all leters are masked
{
mask[i] = i + 127;
printf("\nYour compressed word/sentence is %c ", mask[i]);
}
return;
}
void extract(void) {
printf("\n-------------------------\n");
printf(" EXTRACT ");
printf("\n-------------------------\n");
return;
}
You are scanning only one single character, and as "%c" doesn't skip white-space this is the newline character terminating the previous input.
You instead want to read in a string, and to be on the safe side you should add the maximum length to read to: "%199s" (note: one less than array size to leave space for the terminating null character):
scanf_s("%199s", txt);
Note, too, that as txt is an array it decays to a pointer automatically when being passed to a function (see above); taking the address of (&txt) produces a pointer with the same value, but of a different type: char(*)[200]. This pointer is not compatible to neither %c nor %s format specifier, thus you actually produce *undefined behaviour!
Note, too, that scanf_s (any function from scanf family) will stop reading at the first whitespace – a sentence might, though contain multiple words separated by whitespace. You'd just read the first one of them, though. So you might want to drop scanf for this input entirely in favour of e.g. fgets:
fgets(txt, sizeof(txt), stdin);
Note, here, too, that the previous scanf("%d", ...) did not consume the terminating newline, so you'll need to ignore that, e.g. by a preceding call to getchar.
Crediting this last point to Jonathan Leffler who hinted to in his comment to the question)

I can't input a string in c

So I've tried so much but I can't input a string even using: fgets, gets, scanf, and scanf("%[^\n]%*c",pharse). I need a string with the spaces. It just jumps the code line of input I think.
Please answer with a explanation of why it doesn't work
https://repl.it/#YashKumar11/String#main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const int DIMMAX=100;
char pharse[DIMMAX+1];
int stringLength;
int choice=0;
while(choice != '5'){
printf("1)Enter a new pharse.");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5)\n");
scanf("%d",&choice);
switch(choice){
case 1:
printf("\n=====================\n");
scanf ("%[^\n]%*c",pharse); //<-----------------------It jumps here
printf("\n=====================\n");
stringLength = strlen(pharse);
printf("%s",pharse);
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
printf("\nNot a valid option.\n\n");
break;
}
}
return 0;
}
the statement to input the parase fails because the input for choice leaves a \n in the input stream.
When the second call to scanf() is made, it immediately returns (with a returned value of 0) because the first character input is \n.
suggest following each call to scanf() with:
int ch;
while( (ch = getchar()) != '\n' && ch != EOF );
that also implies that the format string of the second call to scanf() should have the %*c removed.
Your problem is not in the line that you try to read the string, but in the previous call to scanf()
scanf() was written to scanf formatted input. Keyboard input is not that. It can be everything except formatted. The user has over 100 keys to choose from
When the user types a '1' to input a phrase scanf() does not consume the newline. In fact the user can type 1 here we go to enter some text!
and then ENTER. And scanf() will be ok with just the '1'. The rest of the chars would be left there for the program to read. scanf() has no way to know what is left there.
Also scanf() return an int with the number of values read, and it can be zero if the user entered no digits. And you did not tested in your code.
Compare with your code a bit modified below
#define DIMMAX 100
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char phrase[DIMMAX + 1];
int stringLength;
printf("1) Enter a new phrase");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5) Exit\n\nOption: ");
fgets(phrase, DIMMAX, stdin);
while (phrase[0] != '5')
{
switch (phrase[0]) {
case '1':
printf("\n=====================\n");
fgets(phrase, DIMMAX, stdin);
printf("\n=====================\n");
stringLength = strlen(phrase);
phrase[strlen(phrase) - 1] = 0; // deletes the '\n'
printf("Phrase: '%s', len = %zd\n\n", phrase, strlen(phrase));
break;
case '2':
break;
case '3':
break;
case '4':
break;
case '5':
break;
default:
printf("\n'%c' (dec %d) is not a valid option.\n\n",
phrase[0], phrase[0]);
break;
}
printf("1) Enter a new phrase");
printf("\n2)");
printf("\n3)");
printf("\n4)");
printf("\n5) Exit\n\nOption: ");
fgets(phrase, DIMMAX, stdin);
}; // while()
return 0;
}
Maybe it helps to understand.
Note that instead of stopping rigth at the digit, like scanf(), fgets() read up to and including the newline, so if you are using printf() and not puts() to output it, you must take the last byte off the string read

String tokening in C

I have strings like "− · · · −" (Morse code) in an array, and want to tokenize each string to get each individual dot(.) and dash(−). A part of my code is given below:
char *code, *token;
char x;
char ch[4096];
code = &ch[0];
..
while((x = tolower(fgetc(fp))) != EOF){
printf("%c \n", x);
switch(x){
case 'a':
strcpy(code, "· −");
break;
case 'b':
strcpy(code, "− · · ·");
break;
case 'c':
strcpy(code, "− · − · ");
break;
case 'd':
strcpy(code, "− · ·");
break;
case 'e':
strcpy(code, "· ");
break;
case 'f':
strcpy(code, "· · − ·" );
break;
case 'g':
strcpy(code, "− − · ");
break;
case 'h':
}
if(x!= 10){
printf("Value read : %s \n", code);
token = strtok(code, " ");
while(token != NULL){
printf("CHARACTER: %s\n", token);
token = strtok(NULL, " ");
}
}
So, when the code array has "− − ·", I want the output to have:
CHARACTER: −
CHARACTER: −
CHARACTER: ·
However, the output is instead having CHARACTER: − − ·
I am new to string tokenizing, and might made a mistake somewhere there. Perhaps my delimiter is wrong, I am not sure. I hope I have provided enough information. Any help on this would be greatly appreciated.
Thanks in advance
The issue is that the (Unicode) whitespace character in the string literals (e.g. "· · − ·") is different to the whitespace character in the strtok() calls.
Run your source code through xxd and see for yourself.
As far as I can see, the spaces in the strcpy() calls are U+200A whereas the spaces in the strtok() calls are U+0020.
Strtok is not needed thing here (and you don't need those spaces either). If you want the individual characters from the string you could use a simple loop with a pointer over the original string:
char *current=&code;
Then make sure you loop until the end of string (null) character:
while (*current != 0x0) {
if(*current != ' ') {
printf("CHARACTER: %c \n", *current);
current ++;
}
}
What this does:
loops over the characters in code, using current as a pointer, and checking for the null terminator.
It then uses an if to check for a space, and if the character is not a space, format prints it - derefing the pointer to the char there.
Finally it increments the pointer.
Big warning: If you string is not zero terminated (a standard C string will be), this will start printing silly stuff.

scanf and no switch case is executed

New bee in C. This is my code (It replaces a character from a string):
#include <stdio.h>
#include <string.h>
#include <conio.h>
void main()
{
char str[100], r, ra;
printf("enter string");
gets(str);
int length;
length= strlen(str);
printf("length of string is %d",length);
printf("\nenter the the character that will replace");
scanf("%c",&r);
printf("where to replace\n b...begning\ne....ending\np....position");
scanf("%c",&ra);
int pos;
switch(ra)
{
case 'b' : str[1]=r; break;
case 'e' : str[length-1] = r; break;
case 'p' : printf("enter position");
scanf("%d",pos);
if(pos<1 || pos>length-1)
printf("please enter a position between 1 and %d",length-1);
else
str[pos]= r;
break;
}
printf("\n after replacing string is %s", str);
getche();
}
The problem is that the IDE is not compiling this part of the program, I know that I am doing some thing wrong, but can't figure out what? Need help please.
scanf("%c",&ra);
int pos;
switch(ra)
{
case 'b' : str[1]=r; break;
case 'e' : str[length-1] = r; break;
case 'p' : printf("enter position");
scanf("%d",pos);
if(pos<1 || pos>length-1)
printf("please enter a position between 1 and %d",length-1);
else
str[pos]= r;
break;
}
use scanf(" %c",&ra) insted of "%c". Because reading with "%c" give you a garbage value in ra.And that value is new line.
When you enter value in a you press something like p and then Enter key. This Enter key still remains in stdin stream.
Next time when you read in ra then the Enter key in stdin stream is returned in ra.
So for removing that Enter key you need to read like " %c".
scanf(" %c", &ra); // space before %c
Unlike most conversions, %c does not skip whitespace before converting a character. After the user enters the number, a carriage return/new-line is left in the input buffer waiting to be read -- so that's what the %c reads.. SO POST
And for the same reason your switch case is not working, since ra does not have the expected value
the problem is that the ide is not compiling this part of the program
Well, that's a strong accusation. Rather than assume that the compiler does decide not to compile part of the code (on a whim), it's a safer bet that your program's execution flow just does not enter that part as you expected.
In particular, scanf does not behave as you think it does. It reads from stdin, which is a buffered input stream. "Buffered" means that it does not provide your program with input until a newline in read, i.e. until the user presses return. But the scanf family of functions doesn't look for new lines, it treats the new-line character as a normal character. In your case, scanning "%c" tries to read any character from the input. The subsequent "%c" then reads the new line, so &ra really is '\n' in your switch statement.
I usually find working with direct input from the user difficult in C, but if you must prompt the user interactively, I suggest that you read in a whole line of input first with fgets and then analyse that line with sscanf. That gets rid of the seemingly out-of-sync input and also allows you to scan a line several times, perhaps for alternative input syntaxes.
So, here's a version of your code that uses this technique:
#include <stdio.h>
#include <string.h>
int main()
{
char str[100], r, ra;
char line[20];
int length;
int pos;
printf("enter string");
fgets(str, 100, stdin); // note: str includes trailing newline
length = strlen(str);
printf("length of string is %d\n", length);
printf("enter the the character that will replace:\n");
fgets(line, 20, stdin);
sscanf(line, " %c ",&r);
printf("where to replace\n");
printf("b...begning\ne....ending\np....position\n");
fgets(line, 20, stdin);
sscanf(line, " %c ", &ra);
switch (ra)
{
case 'b': str[1] = r;
break;
case 'e': str[length - 1] = r;
break;
case 'p': printf("enter position");
fgets(line, 20, stdin);
sscanf(line, "%d ", &pos);
if(pos < 1 || pos > length-1)
printf("please enter a position between 1 and %d",
length-1);
else
str[pos]= r; break;
}
printf("after replacing string is %s", str);
return 0;
}
There are still problems with your code, mainly to do with zero-based array indexing in C. I leave it to you to sort those out. Also, prefer the safer fgets(buf, len, stdin) over gets(str), which does not prevent buffer overflow. And your query for a position should take a pointer to the address of pos, not just pos. And please make a habit of putting the new-line character last in your printf strings. It makes for cleaner reading and matches the way that the buffered output stream works.
The program doesn't compile, the most likely reason is that you are using a compiler that supports C89 only (I guess it's Visual Studio), or you are using C89 mode.
In this code:
scanf("%c",&ra);
int pos;
switch(ra)
{
the variable pos is defined in the middle of a block, which is supported only since C99. The solution is to move all definitions up to the beginning of a block:
int main()
{
char str[100], r, ra;
int pos;
printf("enter string");
Use fgets() to replace gets(), use int main to replace void main. And fix the problem with using scanf that is covered by the other answers.

Getting values from a mixed string

I am trying to get the values I need from a string using sscanf, but I can't get it done. Here is what I am trying to do:
I have a string which has this pattern
2 7 A BUL
(integer space integer space character space string of 3 elements)
I have to get each value separated by spaces and store it into variables.
This is my code:
sscanf(string[i],"%d %d %s %s",&e,&m,id,modelo);
The problem I'm having is that it only stores the first integers, ignoring the chars.
How can I fix this?
Thank you.
EDIT:
Here's the whole code for the function:
void le_lista(lista *l) {
int e,m;
char id[1],modelo[3],frase[20][12];
int linha=0;
while (1) {
fgets(frase[linha],12,stdin);
//
if (feof(stdin)) {
break;
}
//
linha++;
}
int i;
for(i=0;i<=linha;i++) {
sscanf(frase[i],"%d %d %s %s",&e,&m,&id,&modelo);
enfila(e,m,id,modelo,l);
//printf("%s",frase[i]);
}
printf("Linhas: %d",linha+1);
return;
}
char mystring[] = "2 7 A BUL"
x = strtok(mystring, " "); //separates by spaces
More here Split string with delimiters in C
Given that you want to recognize 2 7 A BUL, you cannot safely use:
int e,m;
char id[1], modelo[3];
sscanf(frase[i], "%d %d %s %s", &e, &m, &id, &modelo);
First, you shouldn't pass char (*)[] values where a char * is expected; do not take the address of an array; pass id and modelo only. GCC will warn you about that if you turn on warnings. If you're learning C, you can't afford not to have the warnings turned on (use -Wall at minimum; -Wall -Wextra if at all possible).
Next, the first %s will read an arbitrary number of characters and null-terminate the result. This is going to overwrite the end of the id array. And you can't safely read 3 characters into modelo either. Because of this, you have two stack overflow problems.
You should write either:
int e, m;
char id[2], modelo[4];
if (sscanf(frase[i], "%d %d %1s %3s", &e, &m, id, modelo) != 4)
...oops...
or perhaps:
int e, m;
char id;
char modelo[4];
if (sscanf(frase[i], "%d %d %c %3s", &e, &m, &id, modelo) != 4)
...oops...
Or, you could use char id[1]; and %c, but that is dangerous; the result is not a null-terminated string.
Your primary input loop is suspect too. You can use feof() as you did, immediately after the fgets(), but it is much more conventional to test the result of fgets() itself; it tells you whether it succeeded or not. That code should probably be:
char frase[20][12];
for (int linha = 0; i < sizeof(frase) / sizeof(frase[0]); i++)
{
if (fgets(frase[linha] ,sizeof(frase[linha]), stdin) == 0)
break;
}
This avoids repeating the 20 or the 12 but protects you from too many lines. It does not protect you from overlong lines; you could add:
size_t len = strlen(frase[linha]);
assert(len != 0);
if (frase[len-1] != '\n')
...input was too long...
to the loop.
You could also think about doing the sscanf() and call to enfila() in the main input loop; you would not then need the frase array to be multi-dimensional.
Putting all the changes together:
char frase[4096];
while (fgets(frase, sizeof(frase), stdin) != 0)
{
int e;
int m;
char id[2];
char modelo[4];
if (sscanf(frase, "%d %d %1s %3s", &e, &m, id, modelo) == 4)
enfila(e, m, id, modelo, l); // l is the parameter to the function
else
...report error...
}
Using fgets() and sscanf() was definitely the correct way to go. It means that error reporting can show the whole line of input, rather than whatever mangled remains scanf() left behind as unreadable.
use instead strtok which is more simpler and IMHO more solid since you can do a simple add error handling
e.g.
int j = 0;
for (char* token = strtok(frase[i]," "; token != NULL; token = strtok(NULL," ")
{
switch(j++)
{
case 0:
e = atoi(token);
break;
case 1:
m = atoi(token);
break;
case 2:
strncpy(id,token,sizeof(id)); // or strcpy_s
break;
case 3:
strncpy(modelo,token,sizeof(modelo)); // or strcpy_s
break;
default:
printf( "invalid number of arguments in line %d\n", i );
break;
}
}

Resources