Don't understand how sscanf() function works - c++

I don't understand how to use sscanf() in C++ for reading from a file.
I'm working on a program that reads three names of runners from a file, along with each of their five best times. I want to average out each runner's times and say who the best runner is. I am having the absolute hardest time with file IO.
Cplusplus.com has helped some, but doesn't really say how to read off, separate the char* (names) from the floats (times), etc. Elsewhere, I can't escape jargon and explanations that are too technical.
A friend showed me this code snippet to explain.
while(file>>str) {
sscanf (str.c_str(),"%d",&myint);
}
Anyone mind explaining to me how it's read?

Using sscanf() is considered a bit old school, but can be very beneficial and is worth mastering. I think you're looking for something like:
sscanf(input, "%s %f", name, &time);
where input is a line from the file. Some of the basics are %s for the basic string and %f for the float.
Enjoy the start of your programming; it only gets better as you gain experience.

scanf if the opposite of printf.
The first "s" in "sscanf" means it scans a string. "fscanf" scans a file, and "scanf" scans the standard input.
the formatting is the same as printf.
if sprintf(s, "%d x", 5) prints "5 x" into s than sscanf("5 x", "%d x", &n) will place 5 into n. It's allways the opposite of printf.
The exception is %s. %s in printf prints EVERY string. printf("%s", str); WILL print str, no matter what's in str. In scanf, %s is a word. A word is something without spaces. so reading a %s will only read a word.
sscanf(str, "%d", &a); means str has a format of "%d" (a single decimal number), and you are reaing this number to a (because &a is the second argument).
As an example
int a,b,c,d;
sscanf("10,20,30,40", "%d,%d,%d,%d", &a, &b, &c, &d);
will result in a = 10, b = 20, c= 30, d = 40. each %d will read to the next argument, and each will read a single decimal number.

Overall, the sscanf() function is used to convert a (C) string into a series of values in program variables under the guidance of a format string which describes the values to be found in the string.
Dissecting the quoted code:
while (file >> str)
{
sscanf(str.c_str(), "%d", &myint);
}
We can assume that file is an input stream and str is a string. The while loop reads one 'word' from the input into the string on each iteration, where a 'word' is a series of non-white space characters, possibly preceded by a series of white space characters.
The sscanf() statement in the example has three arguments and a return value which is ignored (rather at your peril). The first argument is a C style string, so the word that was read is converted to a C string with the str.c_str() call. The second argument is a format string which tells sscanf() what to expect in the string. In this case, the %d conversion specifier indicates a decimal integer. The third argument is a pointer to the corresponding type, where the converted value will be stored. In general, a format string can contain a number of conversion specifiers, and there needs to be one pointer argument for each conversion specifier that assigns (you can skip data by suppressing the assignment).
The return value from sscanf() is the number of successful assigning conversions. In this case, you should be checking that you got one conversion completed.
Here is a working miniature program based on your example:
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
static void read_stuff(std::istream &file)
{
std::string str;
while (file >> str)
{
std::cout << "IN: <<" << str << ">>" << std::endl;
int myint;
if (sscanf(str.c_str(), "%d", &myint) != 1)
{
std::cerr << "Oops: sscanf() failed" << std::endl;
std::exit(1);
}
std::cout << "GOT: " << myint << std::endl;
}
}
int main()
{
read_stuff(std::cin);
return(0);
}
Suppose you type the input line: 123 234 345 abc. Then the program produces:
IN: <<123>>
GOT: 123
IN: <<234>>
GOT: 234
IN: <<345>>
GOT: 345
IN: <<abc>>
Oops: sscanf() failed
Note that if the names you are dealing with contain first name and surname, possibly with middle initial, and with 5 numbers (all on a single line), then you probably need a different process. You'd likely use getline() to read a whole line, and then attempt to parse it with sscanf() (or perhaps you'd use a stringstream instead). You'd have to deal with fewer than 5 numbers on the line, of course. I/O is always tricky, especially when you have to deal with erroneous data too (and production-quality code always has to be ready to deal with erroneous data).

Basically you specify a string format in second argument to sscanf function substituting variables you want to
parse with placeholders.
Say, you know that your file consists of following lines:
Father bought 8 bottles of rum on day 1.
Father bought 11 bottles of rum on day 2.
Father bought 5 bottles of rum on day 3.
Father bought 19 bottles of rum on day 4.
You don't care about anything in that string other than amount of bottles for each day.
You fix the parts that do not change, i.e. "Father bought bottles of rum on day"
and specify a placeholder for parts that you want to extract from the string.
Your code would look like that:
int nDay, nBottles;
sscanf(str.c_str(), "Father bought %d bottles of rum on day %d", &nBottles, &nDay);
cout << "Day: " << nDay << ", bottles: " << nBottles << endl;
The symbol after % just specifies the type of variable that part would be parsed to. d here means decimal.

Simply use sscanf(str.c_str(), "%d", &myint) instead of sscanf(str, "%d", &myint).
I see the others explained about sscanf, but I think your question is a little bit different.
sscanf is a C function, but you can use it in C++ because C++ includes C. sscanf basicly takes a string, divide it as you will show and then assign these parts to variables. But there is no such a thing called string in C. Because of this, sscanf takes its parameter as char* namely char pointer which basicly do the same job with string.
In C++, you should use c_str() function to convert string to char pointer.

Related

How does "to_string" work in array of string?

I want to combine the first 4 characters of each line in txt file and compare it with the keyword I have, but
when I combine the characters, I get the sum of these 4 characters' ascii numbers(whatever).
How can I solve this problem. My code is here:
When I debuged, I saw the string search(variable) was 321.
int main() {
ifstream file("sentence.txt");
if (file.is_open()) {
string line;
while (getline(file, line)) {
string search = to_string(line[0] + line[1] + line[2]); // you see what I mean
if ("dog" == search) {
cout << "there is dog";
}
else {
cout << "there is no dog"<<endl;
}
}
}
}
The function std::to_string() is designed to convert a number into a string representation. It is not what you need.
There is no need to create the new string search to check whether the string line starts with the string "dog".
Creating the new string search is inefficient.
Instead, you could write for example
if ( line.compare( 0, 3, "dog" ) == 0 ) {
cout << "there is dog";
}
else {
cout << "there is no dog" << endl;
}
Or, if your compiler supports C++20, you can also write:
if ( line.starts_with( "dog" ) ) {
cout << "there is dog";
}
else {
cout << "there is no dog" << endl;
}
line[0], line[1], and line[2] are chars, not std::strings. char is an integer type, so adding two chars together results in a single integer that is the sum of the two operands. It does not produce a std::string that is the concatenation of the two chars.
To get a substring of a std::string use the substr member function:
std::string search = line.substr(0, 3);
Or, if you actually need to construct a std::string from individual chars, use the constructor that accepts a std::initializer_list<char>:
std::string search{line[0], line[1], line[2]};
A string made from the first characters of line can be obtained via std::substr. In this case I'd actually prefer the constructor that takes two iterators:
std::string first3chars{line.begin(),line.begin()+3};
Take care of lines that are less than 3 characters.
Your code adds the values of three chars. Adding chars via + does not concatenate them, and if it would why call std::to_string on the result? char is an integer type and what you see as 321 is the result of adding the number representations of the first 3 characters in line.
Is there a way for you to cast those chars (which appear to be integer type for some reason) into char type once again. Perhaps that ought to resolve the issue in case the "to_string" concatenates those 3 inputs into one; additionally intelli-sense should do the trick of explaining parameter usage and returning value.
The problem with this code is that when you access an element of a string you get a character which is an ASCII number, when you try to sum two characters you are adding their ASCII codes.
In your specific case, as you want sequential characters, the best solution would probably be to use the substr function (documentation) for strings. Otherwise, you would probably need to convert one of the characters to a string and then “add” the other characters to it.

What is the C++ equivalent of C language's scanf("%*c")?

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.
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.
"%[^\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'.
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().
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.
It risks undefined behavior due to int overflow. It does not check the return value.
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.
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.

Why when a character array is compared to another character array, output is wrong but when character array is compared to a string output is correct? [duplicate]

This question already has an answer here:
C++ string and string literal comparison
(1 answer)
Closed 1 year ago.
Question - The translation from the Berland language into the Birland language is not an easy task. Those languages are very similar: a berlandish word differs from a birlandish word with the same meaning a little: it is spelled (and pronounced) reversely. For example, a Berlandish word code corresponds to a Birlandish word edoc. However, it's easy to make a mistake during the «translation». Vasya translated word s from Berlandish into Birlandish as t. Help him: find out if he translated the word correctly.
Input -
The first line contains word s, the second line contains word t. The words consist of lowercase Latin letters. The input data do not consist unnecessary spaces. The words are not empty and their lengths do not exceed 100 symbols.
Output -
If the word t is a word s, written reversely, print YES, otherwise print NO.
When I write this code, the output is wrong -
int main(){
char s[100000],a[100000];
cin >> s >> a;
strrev(s);
if(s==a){
cout << "YES";
}else{cout << "NO";}
}
But when I write this code, the output is correct -
int main(){
char s[100000];
string a;
cin >> s >> a;
strrev(s);
if(s==a){
cout << "YES";
}else{cout << "NO";}
}
Why is it like this, is there a rule that a character array cannot be compared to another character array and if so, how can it be compared to a string?
Remember that arrays naturally decay to pointers to their first elements, and it's such pointers that you are comparing.
In short, what you're really doing is:
if(&s[0] == &a[0])
And those two pointers will never be equal.
To compare the contents of character arrays, you need to use strcmp() or similar function instead, eg:
if(strcmp(s, a) == 0)
Since you're programming in C++, please use std::string for all your strings. There are overloads for the == operator that do the right thing if you have std::string values.

How to check if number from stdin is less than the numeric limit of a given type?

I have received a task that does not actually specify, what range of input one of my functions should expect (only, that it is always going to be a positive integer), and the input is decided runtime. Can I somehow test if the type I selected can actually hold the value it was fed to?
An illustration of what I am hoping to do:
char test;
std::cin >> test;
if(MAGIC)
{
std::cout << "Error." << std::endl;
}
With the magical part (or even the preceeding line) being the test I'm looking for.
It should work like this:
stdin: 100 -> no output
stdin: 1000000 -> Error.
For most data types you can check the error state of the input stream as described here. However, this would not work for some other types, such as char from your post, because std::cin >> test would read data as a single character, i.e. '1' which is not what you need to achieve.
One approach is to read the number into std::string, and compare it to the max() obtained through limits:
std::string max = std::to_string(std::numeric_limits<char>::max());
std::string s;
std::cin >> s;
bool cmp = (s.size() == max.size()) ? (s <= max) : (s.size() < max.size());
std::cout << s << " " << cmp << std::endl;
Demo.
Note: The above code makes an assumption that the data entered is a number, which may be arbitrarily large, but must not contain characters other than digits. If you cannot make this assumption, use a solution from this Q&A to test if the input is numeric.
I think the answer may be in https://stackoverflow.com/a/1855465/7071399 to know where to get the limits, then refer to https://stackoverflow.com/a/9574912/7071399 to get inspiration to your code (given you can use a long int or a char array to store the number and then check if it fits in the integer value).

Trying to write a code the corrects a string sentence that is written inside out

I'm a software engineering student, and i need some help with an assignment i was given
i need a code that corrects a sentence written inside out
example;
i love programming
rp evol i gnimmargo
(what i mean by inside out .... that the sentence gets cut into half and each half is flipped)
i need to correct the scrambled sentence
i already started it by counting the charterers in the string that the user enters and cutting the sentence .... but i just cant figure out how to flip each half then join them
any ideas??
#include <iostream>
#include <string>
using namespace std;
//prototype
int NumberOfChar(string testCase);
int main()
{
// declaration
int N, halfCharNum, halfTestCase, size;
string testCase;
// input
cout<<"please enter an integer number, that will represent the number of test cases: "<<endl;
//cin>>N;
cout<< NumberOfChar(testCase)<<endl;
halfCharNum=size/2;
return 0;
}
int NumberOfChar(string testCase)
{
cout << "Enter your string: " <<endl;
getline(cin,testCase);
const int size=testCase.length();
cout << "The total number of characters entered is: " << endl;
return size;
}
This takes all of two lines of C++ code.
1) reverse the string (std::reverse)
2) Rotate the string left n characters where n is half the number of characters in the string. (std::rotate)
First, replace each element in the string with the element on the other end and work your way in.
i.e. in the string "rp evol i gnimmargo" exchange the first character "r" with the last character "o" and then work your way in, next exchanging "p" with "g" and so on.
That leaves you with "ogramming i love pr"
Then, swap the first and second halves of the string.
i.e. "ogramming i love pr" can be split into "ogramming" and " i love pr" -- just swap them for " i love pr" and "ogramming" and combine them
Just use a large dictionary of english words and implement an evolutionary algorithm. Might not be the shortest path to glory but definitely a fun task :-)
Sample dictionaries (you might need anyway) can be found
here,
here,
or here