Splitting C++ strings using a Delimiter - c++

Okay, So I've looked around on StackOverflow and I've stumbled across a way of splitting C++ via delimiters.
So far, I've looked at these, and I still don't understand it.
Parse (split) a string in C++ using string delimiter (standard C++)
https://www.oreilly.com/library/view/c-cookbook/0596007612/ch04s07.html
C++ spliting string by delimiters and keeping the delimiters in result
split a C++ string into two integers, which are delimited by ":"
From my understanding, I need to use a delimiter, using a variable that houses the delimiter, and then use the substr() method/function, but I don't understand the whole thing.
For instance, I saw this one example where it was referencing pos and npos, I don't understand that. And my other issue is, I wouldn't know how to do it with a string with multiple copies of the same delimiter.
My goal is to take a date like this: "29/01/2022 • 05:25:01" to split it into a struct for date and time, eg:
struct Date
{
int day; //Integer for days
int month; //Integer for months
int year; //Integer for years
};
struct Time
{
int hour; //Integer for hour of drop
int minute; //Integer for minute of drop
int second; //Integer for second of drop
int milisecond; //Integer for milisecond of drop
};
I've also looked at https://www.cplusplus.com/reference/, however I want to split it up so that they are stored in their own variables, eg:
string example
{
struct Date D;
struct Time T;
D.Day = 29;
D.Month = 01;
D.Year = 2022;
T.Hour = 5;
T.Minute = 25;
T.Second = 01;
}
Would someone be able to explain this to me in a simpler way, or show me a source that explains it easier? The main problem I have is not understanding certain words.
Any help is appreciated, I really am trying to learn, but I don't quite understand these subjects yet.

Let's go step by step, starting with the date:
29/01/2022 -- Day, Month, Year.
Given the following:
unsigned int day = 0u;
std::cin >> day;
The input of an integer skips whitespace until the first number character (for the first number character, also includes '+' and '-'). The extraction operator keeps reading characters, building a number, until a non-numeric character is reached:
2 --> day.
9 --> day.
The next character is '/', which is not a numeric character so the extraction operator returns the number 29.
The character '/' in this context is known as a delimiter, because it separates the day field from the month field.
Since it's a character, it has to be read using a character variable:
char delimiter = '\0';
std::cin >> delimiter;
Now, the delimiter is no longer in the buffer.
You can check the content of the delimiter variable or move on.
Reading the month is similar:
unsigned int month = 0U;
std::cin >> month;
Edit 1: delimiter and substrings
You could extract the month as a string using a delimiter:
std::string month_as_text;
std::getline(std::cin, month_as_text, '/');
The getline function above reads characters from std::cin, placing into the string month_as_text, until it finds the delimiter character '/'. You can then convert month_as_text into an integer variable.

Related

Reading integer then rest of the line as a single string from input

Suppose I'm trying to read from the following input.txt
6 Jonathan Kim Jr
2 Suzie McDonalds
4 Patty
... and I want to store the first integers from every line and the rest of the strings as a string variable. This is what I have tried:
int num;
string name1, name2, name3;
while ( ins >> num >> name1 >> name2 >> name3 )
{
// do things
}
Unfortunately this won't work since line 2 and line 3 only has 2 and 1 strings in a respective order so the loop will terminate at the very first loop.
Is there a way to store the rest of the strings after an integer in a single variable, including the white spaces? For example, string variable name would hold:
"Jonathan Kim Jr" // first loop
"Suzie M" // second loop
"Patty" // third loop
I thought about using getline to achieve this as well, but that would require me to isolate the integers from the string and I was hoping there's a better approach do this. Perhaps using a vector?
By default, the >> operator splits on a space, so you could use that to pull the integer into the variable. You could then use getline to grab the rest of the line, and store that into the variable.
Example (if you are reading from std::cin):
int num;
std::string name;
std::cin >> num;
std::getline(std::cin, name);
Why don't use regular expressions (https://en.wikipedia.org/wiki/Regular_expression) to parse (https://en.cppreference.com/w/cpp/regex) the input? I think an expression something along the line of 2 subexpressions:
'^([0-1]+)\s([A-Za-z0-9_-/s]+)'

Reading time in hh:mm format from a line

I have a file that consists of lines that look like this:
nameOfFood hh:mm PriceOfFood.
I have to count the income provided between 11:30 and 13:30 by each type of food.
So the output should look like this:
nameOfFood1 yielded $$$ between 11:30 and 13:30.
nameOfFood2 yielded $$$ between 11:30 and 13:30.
and so on.
Currently im using an ifstream f to do this (name and time are strings, price is integer):
f >> name >> time >>price;
My question is: how can I read the HH and MM parts of the timestamp into separate (integer) variables, so I can convert them into minutes (60*HH+MM) to make them comparable?
The way the input operators work is it stops at the point in the stream that no longer matches the target (in addition to on separators, like space). So, just treat the hour and minute as integers in the input stream, but you also have to swallow the colon character.
std::istringstream f("name 19:30 234");
std::string name;
int hour;
int minute;
int price;
char c;
f >> name >> hour >> c >> minute >> price;
Since your input format is fixed, you could do this:
std::string time = ...;
std::string hours(time, 0, 2); // start at 0, take 2 chars
std::string minutes(time, 3, 2); // start beyond ':', take 2 chars
This makes use of std::string's constructor taking another string, position, and character count.
I'll leave the actual integer conversion as an exercise.
simply you can use the c input method:
scanf("%d:%d",&H,&M);
don't forget to include stdio.h

Reading multiple items in data file C++

I'm a C++ beginner. I'm trying to read a file that is formatted like so:
5 Christine Kim # 30.00 3 1
15 Ray Allrich # 10.25 0 0
...
number string # number number number
where each number has its own significance and the name does as well. I can get the file open and read the first two items but after the name I can't get the numbers after. This is my function right now:
void getItems( ifstream& dataFile, // in file
Employee item[], // class array so I can store the data later
int &transNum) // number of transactions
{
int id; // employee ID
char name[20]; // employee name
double hourlyPay; // pay per hour
int numDeps; // number of dependents
int type; // employee type
transNum = 0;
dataFile >> id;
dataFile.ignore(); // discard space before name
dataFile.getline( name, '#');
dataFile >> hourlyPay >> numDeps >> type;
}
I need it to read the first number, read the name, then read the last 3 numbers. After every name (maximum 20 characters) there is a # symbol where we should stop reading the name. I've tried adjusting the size of my char array for my string and other small fixes but nothing works. I realize that I will only get 1 line right now... I was just trying to get the first line to work before I made a loop to grab the other lines.
istream:getline needs a length parameter when used with a char array. Currently it is using '#' as the length value and using the default delimiter.
Change your code to
dataFile.getline( name, sizeof name, '#');
alternatively, use a std:string as the parameter to getline, then you don't need to specify a maximum size.
You're very close. The problem is likely to be the line
dataFile.ignore();
By default, ignore ignores everything up to EOF. What you want to do instead is ignore up to some number of characters until the next space. So that call would instead be:
datafile.ignore(100, ' ');
Where 100 is a purely arbitrary choice of number. Substitute your own rational value.
Next is your use of getline which must be told how many characters to read. Since your buffer is 20 characters, you need to inform getline like so:
getline(name, 20, '#');

C++ string parsing

All:
I got one question in string parsing:
For now, if I have a string like "+12+400-500+2:+13-50-510+20-66+20:"
How can I do like calculate total sum of each segment( : can be consider as end of one segment). For now, what I can figure out is only use for to loop through and check +/- sign, but I do not think it is good for a Universal method to solve this kind of problem :(
For example, the first segment, +12+400-500+2 = -86, and the second segment is
+13-50-510+20-66+20 = -573
1) The number of operand is varied( but they are always integer)
2) The number of segment is varied
3) I need do it in C++ or C.
I do not really think it as a very simple question to most newbie, and also I will claim this is not a homework. :)
best,
Since the string ends in a colon, it is easy to use find and substr to separate out parts of the string partitioned by ':', like this:
string all("+12+400-500+2:+13-50-510+20-66+20:");
int pos = 0;
for (;;) {
int next = all.find(':', pos);
if (next == string::npos) break;
string expr(all.substr(pos, (next-pos)+1));
cout << expr << endl;
pos = next+1;
}
This splits the original string into parts
+12+400-500+2:
and
+13-50-510+20-66+20:
Since istreams take leading plus as well as leading minus, you can parse out the numbers using >> operator:
istringstream iss(expr);
while (iss) {
int n;
iss >> n;
cout << n << endl;
}
With these two parts in hand, you can easily total up the individual numbers, and produce the desired output. Here is a quick demo.
You need to seperate operands and operators. To do this you can use two queue data types one for operands and one for operators
split by :, then by +, then by -. translate into int and there you are.
Your expression language seems regular: you could use a regex library - like boost::regex - to match the numbers, the signs, and the segments in groups directly, with something like
((([+-])([0-9]+))+)(:((([+-])([0-9]))+))+

Validation of string and date input in c++?

I just started c++ today. I am doing some simple registration program. I want to validate the input. I got stuck when validate fullname and birth_date. Here is my requirements:
Fullname: I just want to check if its empty and no punctuation
date_birth: i know this is abit tricky. But if I could validate if the input is valid like: month(1-12), date(1-30) and year (not more than current year) should be enough.
Any quick way to do this?
EDIT:
I tried googled string validation, i am still getting lots of errors. Here is my current code:
string fullname;
do{
cout << endl << "Please enter your fullname";
cin >> fullname;
} while(!ispunct(fullname));
My error message is:
XXXX: no matching function for call to `ispunct(std::string&)'
I already include the library, is this a correct way to check string input. How do you usually do the validation?
EDIT 2:
bool valid;
string fullname;
do{
valid = true;
cout << endl << "Please enter your fullname";
cin >> fullname;
string::iterator it;
for ( it=fullname.begin() ; it < fullname.end(); it++ )
if(ispunct(*it)){
valid = false;
}
} while(!ispunct(fullname));
Its weird, I entered: "!!!", it still by pass. Something is wrong in my code?
Well, I'll try to steer you in the right direction. Firstly, in order to validate the string, you'll need to iterate over it character by character. You can do this using iterators and a for loop. The string class has a begin() and end() method, which you can use to loop over the whole string and examine each character.
Once you're looping over the string, all you need to do is write code to validate it based on your requirements. To make sure there's no punctuation characters, you can use the std::ispunct function, which will tell you whether or not a character is a punctuation character. If you find any punctuation characters, simply consider it an error.
Your first requirement, checking whether the string is empty, is trivial. The string class has an empty() method which returns true if the string is empty.
Validating the birthday is more tricky. This is the sort of thing regular expressions were made for. Unfortunately, C++ has no built-in support for regular expressions (at least not until the next version of the standard). If you're interested, Boost has a good regular expression library in the meantime.
Otherwise, you'll need to loop over the string and validate each character. Make sure the string starts with characters that form a word corresponding to a month name, then make sure a parenthesis falls after that, etc. You'll need to decide how to handle white spaces in between characters. This will be tricky, but it's a good practice exercise to become familiar with C++.
Solution for the second requirement can be trivial if you choose different data type to represent date of birth. Constraints you mentioned here are all numeric (1<= day <= 31, 1 <= month <= 12, 1900 <= year <=2010) and date of birth is basically a set of three numbers so consider using struct type for birth_date variable, something like this:
struct Date{
unsigned int day;
unsigned int month;
unsigned int year;
};
Date birth_date = {3, 12, 1983};
When you pass birth_date to function that performs validation, you just need to compare struct members against limits.