split string - multiple delimiter C++ - c++

so this user input all in one line string , i need to parse the input into two categories: user age and user name.
for example , the user input -->> [23:Frank] [15:Jack] [45:] [33:sofia] []
in this case i have more than one argument (delimiter, total of 3 ) which are [:] , in addition i need to get user input and stop looping once i encounter the [] at the end.
this is what i was thinking :
string input;
vector<string> age;
vector<string> name;
cin >> input;
while (input != "[]")
{
get age between first [ and :
assign to age variable
get name between : ]
assign to user name variable
................
}
also - what if one of the brackets is missing a name , how can assign a blank name and skip that part in order to process the rest (meaning i will output age with no name next to it).
any suggestions regarding how to get and process the data.
i saw some advance stuff like Toknizing and booster which are advance for my course, that's why i was thinking about straight forward getline and parse functions.
Thank you.

Read in token like you are currently doing with cin
test for [] as you are doing with the while loop
For the inside the loop, here are a few things to help you out:
std::string's front and back functions are perfect for ensuring that input starts with [ and ends with ]
std::string's substr function is perfect for trimming off the [] so you can easily ignore them for the rest of the parsing
std::stringstream allows you to call make a stream that only contains your trimmed input.
std::getline(stream, string, char) will read all characters it finds up to the char parameter or the end of the stream and stuff the results in the string parameter and then discard the char it found so you won't trip over it parsing the rest of the input.
strtoul will convert a string into a number and tell you if it failed. It will not accept negative numbers so you can catch people trying to trick your program.
getline(stream, string) will read the stream until it hits an end of line marker. Perfect for reading the rest of a stream that contains no end of lines.
Using strtoul:
char * endp;
unsigned long agenum strtoul(agestr.c_str(), // turn string into old c-style string
&endp, // will be updated with the end of the char after the last number
10); // base ten numbers
if (endp != '\0') // old c-strings always end on a null (numerical zero). If strtoul
// didn't end on a null, the string wasn't a valid number.
{
//not a number
}

Ok , so THANKS for people who helped or at least tried to help!!
what i end up doing for this part was as follows:
read in each string at once
use the find function in order to locate my delimiters ( which are in this case [ : ] )
return positions per each perimeter based on my argument (each pair will hold either the beginning and the end for age || name)
pass those arguments results to truncate the string by using substr function, then assign into each variables.
while (true)
{
string myInput;
cin >> myInput;
while (myInput != "[]")
{
int age_beg = myInput.find('[') + 1 ;
int age_end = myInput.find(':', age_beg);
string age = myInput.substr(age_beg, (age_end - age_beg));
int name_beg = age_end + 1;
int name_end = myInput.find(']', name_beg);
string name = myInput.substr(name_beg, (name_end - name_beg));
cout << "NAME : " << name << " AGE : " << age << endl;
cin >> myInput;
}
}
Hope this will help others with the same question in the future !!

Related

C++ read different kind of datas from file until there's a string beginning with a number

In C++, I'd like to read from an input file which contains different kind of datas: first the name of a contestant (2 or more strings with whitespaces), then an ID (string without whitespaces, always beginning with a number), then another strings without ws and a numbers (the sports and their achieved places).
For example:
Josh Michael Allen 1063Szinyei running 3 swimming 1 jumping 1
I show you the code what I started to write and then stucked..
void ContestEnor::next()
{
string line;
getline(_f , line);
if( !(_end = _f.fail()) ){
istringstream is(line);
is >> _cur.contestant >> _cur.id; // here I don't know how to go on
_cur.counter = 0;
//...
}
}
Thank you for your help in advance.
You should look into using std::getline with a delimiter. This way, you can delimit on a space character and read until you find a string where the first character in a number. Here is a short code example (this seems rather homework-like, so I don't want to write too much of it for you ;):
std::string temp, id;
while (std::getline(_f, temp, ' ')) {
if (temp[0] >= 0 && temp[0] <= '9') {
id = temp;
}
// you would need to add more code for the rest of the data on that line
}
/* close the file, etc. */
This code should be pretty self-explanatory. The most important thing to know is that you can use std::getline to get data up until a delimiter. The delimiter is consumed, just like the default behavior of delimiting on a newline character. Thus, the name getline isn't entirely accurate - you can still get only part of a line if you need to.

Get the line after white-space in C++

I would like to read last few characters in each line of a text file. How would I do that in better optimized C++ code? For example my file has names and ages separated by a white-space of 100 students with new line for each student, so I only want to read the content after white space, i.e age and do further comparison.
File classlist.txt
Tanya 24
Max 16
Dic 21
Code I wrote so far
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
ifstream class_details("classlist.txt");
if (!class_details.is_open())
{
cout<< "error"<<'\n';
return;
}
string data;
% From here I am thinking on how to get the data after first whitespace
% in each line
class_details.close();
return 0;
}
As #Joachim notes, the input operator >> separates on white-space.
You can use it as follows:
string name;
int age;
while (class_details >> name >> age) {
// Use name & age here.
}
class_details >> name extracts a word (non-white-space characters) from the input stream, and stores it in the name variable, and returns the original stream object class_details. This allows chaining the input extraction ...
(Since class_details >> name, returns class_details, we can further apply the >> to the return value of the expression to extract more information.)
>> age continues the extraction, reading (since age is an integer variable) an integer from the input stream, and storing it in the age variable, and (continuing the chain) returns the original stream object class_details.
The expression in the while(...) loop returns (due to the way the >> operators allows chaining) the original input stream, which when evaluated in boolean context, returns the stream's good() state. If the end of stream has been encountered, or an invalid extraction was attempted, or any other problem, good() would return false, and exit the loop.
So, without chaining (but using the , operator instead), the above could be rewritten more verbosely as:
string name;
int age;
while(class_details >> name, class_details >> age, class_details.good()) {
// Use name & age here.
}
EDIT
If the goal is to ignore the name field (skip the name without extracting it into a variable), you could use the ignore() method to skip everything up to the next space character:
int age;
while (class_details.ignore(numeric_limits<streamsize>::max(), ' ') >> age >> ws) {
// Use age here.
}
The >> ws is being used to consume any white-space from the end of the "age" field to the beginning of the name field on the next line ... specifically, the "new line" character.
You can use number smaller than numeric_limits<streamsize>::max(), if desired. Just choose something longer than your longest name.
With my less knowleage
1 : copy data every time to compare if same break
while(1)
{
fgets(str1, sizeof(str1), f); // scanf what you want
if( strcmp(str1,str2)==0 )break;
strcpy(str2,str1);
}
2 : at last add some thing weird
while(1)
{
fgets(str1, sizeof(str1), f); // scanf what you want
if( str1 == "key" )break;
}

Understanding a C++ program [ Bjarne Stroustrup's book ]

i need your precious help for a small question!
I'm reading the Bjarne Stroustrup's book and i found this exemple:
int main()
{
string previous = " ";
string current;
while (cin >> current) {
if(previous == current)
cout << "repeated word: " << current << '\n';
previous = current;
}
return 0;
}
My question is: What does string previous = " "; do?
It initializes previous to the character space (like when you press space). But I thought in C++ it doesn't read it, something about the compiler skipping over whitespace. Why initialize it to that then?
I have tried to write like that: string previous; and the program still work properly... so? What is the differnece? Please enlighten me x)
You seam to be confused on what it means in C++ to ignore whitespace. In C++
std::string the_string = something;
is treated the same as
std::string the_string=something ;
No when you have a string literal the whitespace in the literal is not ignored as it is part of the charcters of the string. So
std::string foo = " ";
Creates a string with one space where as
std::string foo = " ";
Creates a string with 4 spaces in it.
You are right, a whitespace is something you will never get when reading input using std::cin. Therefore, a previous string is initialized with a value that could never (i.e. when reading the first word) possibly match word read into current string.
In this case previous could alsobe initalized to an empty string, because istream::operator>> skips all the whitespace and you would never get an empty like by reading from std::cin that way. However, there are other ways of using std::cin (e.g. together with getline()), which may lead to reading an empty string.
The book explains this example in every detail.
string previous = " ";
assigns a space to the string variable 'previous'.
It may still 'work', but if you were to simply press enter on the first try, the 'repeated word' message should appear.
He could just write :)
string previous;
The idea is that the operator >> can not enter an empty string if by default there is set to skip white spaces.
So any comparison current with an empty string or a string that contains white spaces will yield false.

How to deal with this side effect?

char sign;
cout << "Enter '+' for addition or '-' for subtraction: ";
cin >> sign;
if ( sign != '+' && sign != '-' ) {
cout << "you can only enter '+' or '-'!\n";
return 1;
}
If I enter +32423, the input is still correct to the cin(it will pass the checking), because + will be automatically set to sign variable, and also 32423 will be stored into my next variable if there is:
cin >> number;
How can I change the code so that +32423 is incorrect to the cin?
You need to decide exactly what the input you require from the user is and what makes it valid. It sounds to me like you want to accept an entire line of input from the user (so you should use std::getline with a std::string) and then you want to only accept strings that are exactly "+" and "-".
std::string sign;
std::getline(std::cin, sign); // Gather input
if (sign != "+" && sign != "-") { // Parse input
// ...
}
// ...
As #H2C03 mentioned, it's a good idea to split up the inputting and the parsing. In this case, the parsing is as simple as comparing the input line to the strings "+" and "-".
Don't try to do two things in one step. This is a common error -- don't feel too bad, the designers of the scanf() family of funcionts and std::istream::operator>> made the exact same mistake.
What you are trying to do: get user input and parse it in order to perform a computation.
What you are actually doing: you are doing these inhrenetly different things in one step, and this is making you confused. You should clearly and cleanly separate the two steps instead: first, get the user input, and the parse it properly. As in:
// get an entire line
std::string line;
std::getline(std::cin, line);
// parse it: only the format <some digits> <'+' or '-'> <other digits>
// is acceptable, most probably with interspersed whitespace
std::string::iterator it = line.begin();
while (isspace(*it) && it != line.end())
it++;
std::string::iterator it2 = it;
while (isdigit(*it) && it != line.end())
it++;
std::string first_number(it2, it); // construct a new substring for the 1st number
// etc. continue the parsing in a similar manner

How to check the length of an input? (C++)

I have a program that allows the user to enter a level number, and then it plays that level:
char lvlinput[4];
std::cin.getline(lvlinput, 4)
char param_str[20] = "levelplayer.exe "
strcat_s(param_str, 20, lvlinput);
system(param_str);
And the level data is stored in folders \001, \002, \003, etc., etc. However, I have no way of telling whether the user entered three digits, ie: 1, 01, or 001. And all of the folders are listed as three digit numbers. I can't just check the length of the lvlinput string because it's an array, so How could I make sure the user entered three digits?
Why not use std::string?
This makes storage, concatenation, and modification much easier.
If you need a c-style string after, use: my_string.c_str()
Here is a hint: To make your input 3 characters long, use std::insert to prefix your number with 0's.
You are really asking the wrong question. Investigate the C++ std::string class and then come back here.
Eh? Why do they need to enter 3 digits? Why not just pad it if they don't? If you really want to check that they entered 3 digits, use strlen. But what I recommend you do is atoi their input, and then sprintf(cmd, "levelplayer.exe %03d", lvlinput_as_integer)
Here's how you could do this in C++:
std::string lvlinput;
std::getline(std::cin, lvlinput);
if (lvlinput.size() > 3) { // if the input is too long, there's nothing we can do
throw std::exception("input string too long");
}
while (lvlinput.size() < 3) { // if it is too short, we can fix it by prepending zeroes
lvlinput = "0" + lvlinput;
}
std::string param_str = "levelplayer.exe ";
param_str += lvlinput;
system(param_str.c_str());
You've got a nice string class which takes care of concatenation, length and all those other fiddly things for you. So use it.
Note that I use std::getline instead of cin.getline. The latter writes the input to a char array, while the former writes to a proper string.
What do you mean you can't check the length of the string? getline generates a NULL terminated c-string so just use strlen(lvlinput).
Neil told you where you should start, your code might look like this.
std::string level, game = "levelplayer.exe ";
std::cout << "Enter the level number : ";
std::cin >> level;
if(level.size() != 3)
{
// Error!
}
else
{
// if you have more processing, it goes here :)
game += level;
std::system(game.c_str());
}
You can check the length of your NULL terminated string that getline returns by using:
int len = strlen(lvlinput);
This works because getline returns a NULL-terminated string.
However, this is besides the point to your problem. If you want to stay away from std::string (and there isn't any particular reason why you should in this case), then you should just convert the string to an integer, and use the integer to construct the command that goes to the system file:
char lvlinput[4];
std::cincin.getline(lvlinput, 4);
char param_str[20];
snprintf(param_str, 20, "levelplayer.exe %03d", atoi(lvlinput));
system(param_str);