I'm working on a Windows-driven C++ project now. And there is a function which composes a file names with date portion in them. It uses the wcsftime C runtime library function to format the date portion with the "%x" formating code. This code corresponds to the
%x Date representation for the locale
as the documentation says.
Now, I need to check which files match to some dates-range. So, I need to parse this date portion of the file name back to the date and perform the comparing. But, I'm in trouble: how to do this? I didn't find any CRT function which can do the work. Furthermore, it could be much more better for me to get the formating string alike the "dd-mm-yyyy" for the current locale in order to parse this string. But I didn't find any way how to do this.
I pay your attention, that it's about the CRT functions. Its locale differs from the locale used by the GetLocaleInfo, for example.
One easy solution is to go forward : Generate a filename using the same pattern, but for a test date of 2001-02-03. The result will tell you the order of year,month and day.
The big question is, if you know the locale with which the date in the string was created.
If you are on the same machine then you can use the time_get function described here.
With that you can get the order of the day, month, year in the date. Having that you can then first build a std::regex to extract the digits and then assign the digits into the corresponding date segments.
A regex could look like R"(\d{1,4}[.\/,;]\d{1,4}[.\/,;]\d{1,4})" which will match 3 digit groups separated by some typical date separators.
Then, with the know how of the date order, you can extract day, month and year from a string.
Some example code:
#include <iostream>
#include <locale>
#include <string>
#include <regex>
#include <iomanip>
// Example for year/month/day formated string
std::string fileName{"file7_234x2021/5/23abc.txt"};
std::regex re{R"((\d+)[\.\\\/\,\;](\d{1,4})[\.\\\/\,\;](\d{1,4}))"};
int main() {
std::smatch sm{};
if (std::regex_search(fileName,sm,re)) {
int year = std::stoi(sm[1]);
int month = std::stoi(sm[2]);
int day = std::stoi(sm[3]);
std::cout << std::right << std::setfill('0') << std::setw(2) << day << '-' << std::setw(2) << month
<< '-'<< std::setw(4) << year << '\n';
}
}
If you do not know the date order, then you may have a big problem and can only find out with some heuristic methods what is what.
E.g.
Numbers above 31 are years
Numbers above 12 are days (or years)
But, somehow error prone . . .
This question already has answers here:
Difference between erase and remove
(7 answers)
Closed 2 years ago.
I recently tried a code mentioned somewhere on this site to remove blank spaces in a string. The answer suggested the function remove from the algorithm library (amazingly explained here: https://www.geeksforgeeks.org/stdremove-stdremove_if-c/) but it gives an unexpected output. It replaces all the blank spaces with some random numbers. Here is the code.
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string a;
int b;
getline(cin, a);
remove(a.begin(), a.end(), ' ');
b = stoi(a);
cout << b << endl;
return 0;
}
If I input 14 546 32 for example it outputs 145463232. Oddly enough if I input 1 2 3 4 5 it outputs the correct thing: 12345.
Expected output, input:
I input any number with blank spaces in between some numbers.
It outputs the number without spaces.
I tried compiling it online with this compiler: https://www.onlinegdb.com/. It has the exact same output. Can anybody figure out what is wrong with the code. And also i need to turn the string into an integer to do some mathematical operations with the integer afterwards (that is why I use the stoi function). Thanks.
Interesting thing about std::remove is that it does not actually remove anything. =)
You will need to erase the character by yourself like this:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string a;
int b;
getline(cin, a);
a.erase(remove(a.begin(), a.end(), ' '), a.end());
b = stoi(a);
cout << b << endl;
return 0;
}
You can read more about Erase–remove here:
https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom
std::remove removes the elements that compare equal to its argument by shifting the remaining elements using move-assignment. It doesn't resize the container after shifting those elements. In your case, since the objects are of type char, they are simply copied to the beginning of the array. So the end of your string stays the same.
When you remove spaces from the first number, and shift everything to left, you get this:
old string: 14 546 32
new string: 145463232
When you do the same in the second case:
old string: 1 2 3 4 5
new string: 12345 4 5
The only reason you get the correct result in the second one is the space in-between. First number in the string (12345) is converted to int and returned.
Note that std::remove also returns an iterator to the new end of the list. You could use that to resize the string.
auto end = remove(a.begin(), a.end(), ' ');
a.resize(end - a.begin());
I'm trying to understand how can I convert multiple integers from a string. I have tried to use atoi(), stoi() and istringstream() and they all do the same thing. I can't get more than one integer.
For example, if the string is "Chocolate and Milk 250 pounds and 2 oz or 1.5 coins."
All of the above functions will not work. It won't take the whole string. If I leave only a single number then it will work. I want to be able to read the whole string and get all of the integers only (not float).
I am using a while(getline()) for the string. Then try to get it into string.
Although, if I could only return the total amount of integers in the string that would be better. Either way, I'm trying to learn both ways. In this case, the output would be "2", since there are only two int.
One way is to split the string using as delimiter and using stoi on individual strings to check if they are integers.
#include <iostream>
#include <sstream>
#include <string>
int main(){
std::string s = "Chocolate and Milk 250 pounds and 2 oz or 1.5 coins.";
int count = 0;
std::istringstream iss(s);
std::string token;
while(getline(iss,token,' ')){
if(std::isdigit(token[0]) &&stoi(token) && token.find('.')==std::string::npos){
count++;
}
}
std::cout<<count<<std::endl;
}
Note that more complex checks can be done on strings if stoi succeeds, but the input is not a valid integer. You can have a helper function which checks if all the characters are digits or not by using isdigit etc.
my input file looks like this :
S New York 25 76 49
i want to read them where S is a character and New York is either a string or a cstring and the other 3 are integers. My problem is reading in New York i cant use getline since the 3 integers come after it and not in a new line. What can i do?
I would suggest using Regular Expressions to parse the input. Added to the standard library in C++ 11 <regex> C++ reference
More details on wikipedia: Regular Expressions in C++
Your other option is simply to read a character at a time and as long as the character isalpha() or isspace() followed by another isalpha(), store the character in your string. For example:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main (void) {
char c, last = 0;
string s;
while (cin.get(c)) { /* read a character at a time */
/* if last is alpha or (last is space and current is alpha) */
if (last && (isalpha(last) || (isspace(last) && isalpha(c))))
s.push_back(last); /* add char to string */
last = c; /* set last to current */
}
cout << "'" << s << "'\n";
}
Example Use/Output
$ echo "S New York 25 76 49" | ./bin/cinget
'S New York'
It may not be as elegant as a regex, but you can always parse what you need simply by walking through each character of input and picking out what you need.
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.