-2

Objective :

  • Simplest and similar way to discard any unwanted (which in my case is every non numeric characters but I'd like a solution for a more general case) and remove them from buffer. Example,
#include<stdio.h>
void main(void){
    int num1, num2;
    
    printf("Enter num1 here : ");
    scanf("%d%*[^\n]%*c", &num);//First scanf statement.
    
    printf("Enter num2 here : ");
    scanf("%d", &num2);//Second scanf statement.
    
    printf("num1  is : %d\n
            num2 is : %d", num, num2);
}

    /* OUTPUT */
    1st number : 25 asdfasfasfasf
    2nd number : 30
    Input 1 : 25
    Input 2 : 30
    /* This will work by discarding any non */
    /* In the program above I didn't write a printf statment to display the text "1st num, 2nd num". This is just so the output is a bit more clear. I am aware of it.  */

Now if you change the first scanf from scanf("%d%[^\n]%*c"); to scanf("%d"); and give the same input, you'd get the following output :

#include<stdio.h>

void main(void){
    int num1, num2;

    printf("Enter num1 here : ");
    scanf("%d", &num1);
    
    printf("Enter num2 here : ");
    scanf("%d", &num2);

    printf("num1 : %d\nnum2 : %d", num1, num2);
}
    //OUTPUT//
    Enter num1 here : 11 asdf
    Enter num2 here : num1 : 11
    num2 : 32764
   /*As you might see, I am not prompted for 2nd input. Instead the already present characters in the buffer are used.*/

Briefing :

  • In C scanf("%d%[^\n]%*c"); would remove all the redundant characters like spaces, newline, alphanumeric that come after the number form the buffer before/ after taking another input. How can I achieve the same in C++ where after my cin >> var; would take the remaining characters in the buffer and then discard them. This only works for numbers from 0-9. But what I am interested in is the *[^\n] and *c because it helps me read the characters from buffer without saving them anywhere which technically means that they get discarded.

Exclusions :

  • cin >> ws;
  • cin.ignore(numeric_limits::max(),’\n’);

I've found the aforementioned ways but unless there is no other more viable option available I'd rather not use these since it involves including external libraries #limits and #vector receptively.

Aproximation :

  • cin.ignore() is the closest and I think it would suffice.
  • 11
    What's wrong with `#include ` and `#include `? These are not external libraries but they are part of the C++ standard. And I see no vector in your code. – Jabberwocky Jan 26 '22 at 11:28
  • 5
    Your title, your objective, and your briefing are talking about three different things. Please specify clearly what you are looking for. Title is `"%*c"`, objective is "discard non-numeric characters", and briefing is `"%^[\n]%*c"`. None of these match the others. Also, `${var}` is not valid C++. – DevSolar Jan 26 '22 at 11:29
  • 2
    What about `std::scanf("%^[\n]%*c");` ? – Ted Lyngmo Jan 26 '22 at 11:30
  • If you don't want to include `` you can replace `numeric_limits::max()` by any big number, it is the maximum number of characters in the line, so `cin.ignore(2000000000, '\n');` is fine too, though it looks a little bit confusing because of the random number. – mch Jan 26 '22 at 11:41
  • `%^...` is not a valid conversion specifier, I assume you want `%[^\n]` there. And then you're running into undefined behavior because you are not giving an argument to assign the result set to. – DevSolar Jan 26 '22 at 11:43
  • @Jabberwocky Is the vector library not necessory? The [reference GeeksForGeeks] (https://www.geeksforgeeks.org/clearing-the-input-buffer-in-cc#highlighter_813549) that I was using used it so I assumed it was compulsory (blunder on my part, should have cross checked). And in my project I am limited on the total libraries that I can use which is why I was avoiding #vector and #limits. – user17007422 Jan 26 '22 at 11:44
  • 2
    @user17007422: `#vector` and `#limits` are not libraries, nor valid C++. I again assume that you mean `#include ` and `#include `. To reiterate, those are part of the C++ standard library, i.e. not external. If you are currently enrolled in a C++ course that teaches you C++ by teaching you C and disallowing large swaths of the standard libraries "for educational purposes", resign and find a better course. – DevSolar Jan 26 '22 at 11:46
  • @TedLyngmo I was told by my prof that it is not recommended to use C functions in CPP. Should I believe otherwise? – user17007422 Jan 26 '22 at 11:46
  • 3
    @user17007422: Generally good advice. The main problem is that your question is rather unclear about what the actually desired result of the operation should be (see my first comment). – DevSolar Jan 26 '22 at 11:51
  • @user17007422 no, `#include ` is not necessary in the the [sample on geeksforgeeks](https://www.geeksforgeeks.org/clearing-the-input-buffer-in-cc#highlighter_813549). – Jabberwocky Jan 26 '22 at 12:18
  • @DevSolar {1/4} ReplyOfComment[0] : Sorry for the trouble and thanks for the reply mate! I recommend you please clearly specify why they are all different. Title is %*c because what it does is reads all and any char and instead of saving it, it discards it. Hence eliminating it from the buffer. Objective is ***discard*** any non-numeric characters, that are ***redundant***. which is done by ```*c```, Hence the title. And briefing (which is what I believe what confused you. I should have been more specific. Sorry about that.) says ```%[^\n]%*c``` which is a more general code. – user17007422 Jan 26 '22 at 17:53
  • @DevSolar {2/4} Continuing[0] : It won't do anything on its own. You have to prefix the ```[^\n]``` with something. That something in my case is ```%d``` but it could be something else in your case. To clarify, I have put some code in my original post which you might want to look at. ReplyOfComment[1] : Yeah you'd be right to assume that. ```^[\n]``` was a typo. Also the ${var} was a shorthand for the reader. Pseudo code if you will. You put whatever you want there. ```[^\n]``` is supposed to be ```*[^\n]```. – user17007422 Jan 26 '22 at 17:54
  • @DevSolar {3/4} ReplyOfComment[2] #vector and #limits were again a shorthand. I'd stop using pseudo code from now own since I have seen these can cause misunderstanding. ReplyOfComment[3] Let me know if it still requires any editing. I have tried to make it more clear by providing examples. ReplyOfComment[4] I am aware of it. ```%*c``` is there for just that. Taking all characters that have been left over on the buffer and discard them. The ```scanf("%d%*[\n]%*c)``` has 3 parts. The first part ```%20d``` reads all the first N digits that are specified by prefixing a number before the "d". – user17007422 Jan 26 '22 at 18:12
  • @DevSolar {4/4} The second part ```%*[^\n]``` reads all the leftover characters that haven't been read by the ```%d``` BUT (and this is important) it leaves a newline character without reading in the buffer. This is where the ```%*c``` comes in. It reads any and all characters, which in my case is the newline, and then discards it. The ```%*c``` is crucial. Without it your 2nd input will just me the ASCII for the newline which is 10. Sorry for the late reply. Let me know in case of any more clarification. – user17007422 Jan 26 '22 at 18:13
  • 2
    Well, if the code you're posting has nothing in common with the question you're asking, you have to expect that kind of comments. {Shrug} A more general suggestion would be to read full lines using `fgets` and then parse the input in memory with `strtol`, `strpbrk` et al -- `scanf` is generally ill-equipped to parse potentially malformed input. Or, indeed, use C++.... – DevSolar Jan 26 '22 at 18:38
  • 2
    Also note that all other users have queried for similar clarification. Please attempt to write better questions in the future, and you will be met with faster and better answers -- and less hassle in the comments. Have a good day. – DevSolar Jan 26 '22 at 20:06
  • 1
    @user17007422 "{4/4} The second part %*[^\n] reads all the leftover characters that haven't been read by the %d BUT (and this is important) it leaves a newline character without reading in the buffer. This is where the %*c comes in. " has a hole. When the character after the numeric text is a `'\n'`, it does not match `"%*[^\n]"`, so scanning stops. The following "`%*c"` is not encountered. – chux - Reinstate Monica Jan 27 '22 at 03:11
  • @chux-ReinstateMonica When the character after the numeric text is a ```\n``` the buffer would automatically stop taking input for that variable since it would be considered as if the enter key was pressed and it would then move onto the next scanf. No? – user17007422 Jan 27 '22 at 08:33
  • @user17007422 Your goal with variations on `"%*^[\n]%*c"` is to "discard any unwanted ... characters ... and remove them from buffer". When `'\n'` is the first character after the numeric text, the `'\n'` _remains_ in `stdin`, the next `scanf()` would start with that remnant of the previous line and not the first character on the next entry. – chux - Reinstate Monica Jan 27 '22 at 20:32

2 Answers2

3

In C scanf("%[^\n]%*c"); would remove all the redundant characters like spaces, newline, alphaNumerics form the buffer before/ after taking another input.

This is amiss in so many ways.

  1. "%[^\n]%*c" scans 1 or more non-'\n' (Attempting to save them) and then 1 '\n'. There must exist a leading non-'\n' else the scanning stops. There is nothing special about spaces and alphanumeric - just '\n' and non-'\n'.

  2. Undefined behavior: "%[^\n]" lacks a matching pointer to save the input. Even with a matching char *, it lacks a width and is prone to buffer overflow. It is worse than gets().

  3. With an input of only "\n", nothing is consumed, nothing is saved. scanf("%[^\n]%*c"); fails to consume anything if the first character is a '\n'. Without checking the return value, the calling code does not know if anything was read. The matching char *, if it was there, is unchanged or potentially indeterminate.

Do not use scanf("%[^\n]%*c"); or its C++ equivalent std::scanf("%[^\n]%*c");


OP's update to scanf("%d%*[^\n]%*c", &num); is still faulty and should not be used in robust code.

  1. It risks undefined behavior due to int overflow. It does not check the return value.

  2. It does not consume the rest of the line when the numeric text is followed by a '\n'. That '\n' remains in stdin.


A better C approach begins with fgets() and checking return values.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • the ```scanf("%*[^\n%*c");``` needs to be prefixed with a format specifier of choice. which in my case is the ```%d``` -> ```scanf("%5d%*[^\n%*c");``` I have edited my main post. That should clear the misunderstanding. – user17007422 Jan 26 '22 at 18:18
  • 4) I mentioned it in [the comment here](https://stackoverflow.com/questions/70862682/what-is-the-c-equivalent-of-c-languages-scanfc?noredirect=1#comment125284183_70862682) that you'd use some number depending on what type of value you are use, i.e. float, int, double, long int, etc and how much digits you want. As for 5) I replied to your comment [here](https://stackoverflow.com/questions/70862682/what-is-the-c-equivalent-of-c-languages-scanfc?noredirect=1#comment125296809_70862682) – user17007422 Jan 27 '22 at 08:42
  • @user17007422 The key point I do not think you see yet is that scanning with `"%d%*[^\n]%*c"` does not always reach the last `"%*c"`. – chux - Reinstate Monica Jan 27 '22 at 20:34
  • I see what you mean now. If there are multiple newline characters, let's say 5, then the ```*c``` would only discard one, leaving 2 in the buffer. Right? Anyhow, the point of the question was to get something that does something similar but in C++, not in C. While also being a non C function. I am not using the scanf in my C++ to begin with. Although I do see the error in my ways and I actually spent yesterday changing all my scanf statements in my C programs. For C++ somebody suggested using the ```cin.ignore(n, x)``` which is the closest to the scanf. – user17007422 Jan 28 '22 at 08:54
  • 1
    @user17007422 "`"%*c"` would only discard one ..." --> No., it is not certain it would discard any. With format `"%d%*[^\n]%*c"` and input `"123\n"`, the `'\n'` remains in `stdin` as scanning stopped at `"%*[^\n]"` and does not proceed to attempt `"%*c"`. – chux - Reinstate Monica Jan 28 '22 at 22:12
0

Using C++20 onwards, you might want to use std::format. This is basically an implementation of FMT into the standard C++.

For more complex scenarios regular expressions should help.

Chris G.
  • 816
  • 2
  • 15