i was wondering if this was a safe way of splitting up string into specific variables. I am currently not getting the correct results. Need newFirstName to contain Joe. newLast name to contain Robbin. NewTeam to contain Oilers and so on. What i am currently getting is nothing in all of the variables except for newFirstname. A point in the correct direction would be much appreciated.
string line = "Joe|Robbin|Oilers|34|23";
char* strToChar = NULL;
char* strPtr = NULL;
string newFirstName = " ";
string newLastName = " ";
string newTeam = " ";
int newAssists = 0;
int newGoals = 0;
sscanf(line.c_str(), "%[^|]s%[^|]s%[^|]s%d|%d",(char*)newFirstName.c_str(), (char*)newLastName.c_str(), (char*)newTeam.c_str(), &newGoals, &newAssists);
Saw lots of great answers but before i did i came up with:
string line = "Joe|Robbin|Oilers|34|23";
char* strToChar = NULL;
char* strPtr = NULL;
string newFirstName = " ";
string newLastName = " ";
string newTeam = " ";
int newAssists = 0;
int newGoals = 0;
int count = 0;
std::string delimiter = "|";
size_t pos = 0;
std::string token;
while ((pos = line.find(delimiter)) != std::string::npos)
{
count++;
token = line.substr(0, pos);
std::cout << token << std::endl;
line.erase(0, pos + delimiter.length());
switch (count)
{
case 1:
newFirstName = token;
break;
case 2:
newLastName = token;
break;
case 3:
newTeam = token;
break;
case 4:
newGoals = atoi(token.c_str());
break;
}
}
newAssists = atoi(line.c_str());
To split a string by some delimiter other than space, the simplest is to use a stringstream along with std::getline, which permits to specify your delimiting character.
std::istringstream iss(line);
std::getline(iss, newFirstName, '|');
std::getline(iss, newLastName, '|');
std::getline(iss, newTeam, '|');
iss >> newAssists;
iss.ignore(); // drop the '|'
iss >> newGoals;
The way you were doing it has many problems indicated in the comments. Most important error was trying to write directly in a std::string's buffer through sscanf, which breaks the most important rule of data encapsulation and leads to undefined behavior.
I was wondering if this was a safe way of splitting up string into specific variables.
My suggestion:
Use std::istringstream and std::getline to tokenize the line.
Pull the numbers of out of the tokens using std::stoi.
Here's a working example.
#include <iostream>
#include <sstream>
#include <string>
int main()
{
std::string line = "Joe|Robbin|Oilers|34|23";
std::istringstream str(line);
std::string newFirstName;
std::string newLastName;
std::string newTeam;
std::string newAssistsString;
std::string newGoalsString;
std::getline(str, newFirstName, '|');
std::getline(str, newLastName, '|');
std::getline(str, newTeam, '|');
std::getline(str, newAssistsString, '|');
std::getline(str, newGoalsString);
// Sanity check.
if (!str )
{
std::cout << "Unable to extract the tokens from the line\n";
return EXIT_FAILURE;
}
int newAssists = std::stoi(newAssistsString);
int newGoals = std::stoi(newGoalsString);
std::cout
<< newFirstName << " "
<< newLastName << " "
<< newTeam << " "
<< newAssists << " "
<< newGoals << std::endl;
return EXIT_SUCCESS;
}
Output
Joe Robbin Oilers 34 23
Related
I get a line like: "1001", Name
I want to know how to grab the number in between the quotes without atoi.
The problem asks to make the function just grab the integer that's between two quotes in a string, then grab the name and place it in a string, but I don't understand that part.
Search using the regular expressions:
#include <regex>
#include <iostream>
int main()
{
const std::string s = "\"1001\", John Martin";
std::regex rgx("\"(\\d+)\", *([\\w ]+)"); // this will extract quoted numbers in any string
std::smatch match;
if (std::regex_search(s.begin(), s.end(), match, rgx))
std::cout << "ID: " << match[1] << ", Name: " << match[2] << '\n';
}
Have a look at std::istringstream, eg:
std::string s = "\"1001\", Name";
std::string name;
int num;
std::istringstream iss(s);
iss.ignore();
iss >> num;
iss.ignore();
iss.ignore();
std::getline(iss, name);
Or
std::string s = "\"1001\", Name";
std::string name;
int num;
std::istringstream iss(s);
iss.ignore(std::numeric_limits<std::streamsize>::max(), '"');
iss >> num;
iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
std::getline(iss, name);
Or
std::string s = "\"1001\", Name";
std::string name;
int num;
std::string::size_type start = s.find('"') + 1;
std::string::size_type end = s.find('"', start);
std::string snum = s.substr(start, end - start);
std::istringstream(snum) >> num;
start = s.find(',', end+1) + 1;
start = s.find_first_not_of(' ', start);
name = s.substr(start);
You can also make use of the std::string functions find, find_first_not_of, and substr to parse the information.
You simply work your way down the original string finding the opening quote ", storing the index, then finding the closing quote, and its index, the integer string is the characters in between.
Next, you can use find_first_not_of locating the first character not a ", \t" (comma, space, tab), taking the name as the remainder of the original string.
#include <iostream>
#include <string>
int main (void) {
std::string s = "\"1001\", Name", ssint, ssname;
size_t begin, end;
begin = s.find ("\""); /* locate opening quote */
if (begin == std::string::npos) { /* validate found */
std::cerr << "error: '\"' not found.\n";
return 1;
}
end = s.find ("\"", begin + 1); /* locate closing quote */
if (end == std::string::npos) { /* validate found */
std::cerr << "error: closing '\"' not found.\n";
return 1;
}
ssint = s.substr (begin + 1, end - 1); /* int is chars between */
begin = s.find_first_not_of (", \t", end + 1); /* find not , space tab */
if (begin == std::string::npos) { /* validate found */
std::cerr << "error: no non-excluded characters found.\n";
return 1;
}
ssname = s.substr (begin); /* name is reamining chars */
std::cout << "int : " << ssint << "\nname: " << ssname << '\n';
}
(note: always validate the results of find and find_first_not_of by ensuring the return was not std::string::npos)
Example Use/Output
$ ./bin/parse_str
int : 1001
name: Name
You can find details on all of the string library member functions at cppreference - std::basic_string Let me know if you have any questions.
Ok, so here is the text of the file I am trying to read:
KEYS
a set of keys
3
LAMP
a brightly shining brass lamp
8
ROD
a black rod with a rusty star
12
Ok, so pretend that each line is evenly spaced, but there are 2 blank lines, (or tabs) between 8 and ROD. How would I skip that and continue with the program? I am trying to put each line into 3 vectors (so keys, lamp, and rod into one vector etc). Here is my code (but it does not skip the blank line).:
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <fstream>
using namespace std;
int main() {
ifstream objFile;
string inputName;
string outputName;
string header;
cout << "Enter image file name: ";
cin >> inputName;
objFile.open(inputName);
string name;
vector<string> name2;
string description;
vector<string> description2;
string initialLocation;
vector<string> initialLocation2;
string line;
if(objFile) {
while(!objFile.eof()){
getline(objFile, line);
name = line;
name2.push_back(name);
getline(objFile, line);
description = line;
description2.push_back(description);
getline(objFile, line);
initialLocation = line;
initialLocation2.push_back(initialLocation);
} else {
cout << "not working" << endl;
}
for (std::vector<string>::const_iterator i = name2.begin(); i != name2.end(); ++i)
std::cout << *i << ' ';
for (std::vector<string>::const_iterator i = description2.begin(); i != description2.end(); ++i)
std::cout << *i << ' ';
for (std::vector<string>::const_iterator i = initialLocation2.begin(); i != initialLocation2.end(); ++i)
std::cout << *i << ' ';
I think you can check to see if the string is empty thru std::getline. If it is, then you can ignore it or something like.
getline(objFile, line);
name = line;
while(name.length() == 0)
{
getline(objFile, line);
name = line;
}
name = line;
name2.push_back(name);
getline(objFile, line);
description= line;
while(description.length() == 0)
{
getline(objFile, line);
description = line;
}
description= line;
description2.push_back(description);
getline(objFile, line);
initialLocation = line;
while(initialLocation.length() == 0)
{
getline(objFile, line);
initialLocation = line;
}
initialLocation = line;
initialLocation2.push_back(initialLocation );
If i am correct then a line will have no length if it is blank and if it is we check again therefore ignoring it.
You can use std::getline() (As pointed out by many people) instead... which will yield each line one-by-one:
inline std::string ReadFile(std::ifstream& stream)
{
std::string contents;
std::string temporary;
while (std::getline(stream, temporary))
if (!std::all_of(temporary.begin(), temporary.end(), isspace))
contents.append(temporary + "\n");
return contents.substr(0, contents.size() - 1);
}
And yes, an example:
int main()
{
std::ifstream is("file.txt");
if (is.fail())
return 1;
auto const contents = ReadFile(is);
is.close();
std::cout << contents << std::endl;
std::cin.get();
return 0;
}
This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 6 years ago.
I am reading in a file, that contains data in this format on each line. 30304 Homer Simpson I need to be able to pass this to the following constructor, the integer being the regNo, the name the rest of the string, and every student would have their own map of marks.
Student::Student (string const& name, int regNo):Person(name)
{
regNo = regNo;
map<string, float> marks;
}
I then have to add each student to a collection of students, which would be best, and how do I do this?
So far all I've got is getting the file name and checking it exists.
int main()
{
//Get file names
string studentsFile, resultsFile, line;
cout << "Enter the Students file: ";
getline(cin, studentsFile);
cout << "Enter the results file: ";
getline(cin, resultsFile);
//Check for students file
ifstream students_stream(studentsFile);
if (!students_stream) {
cout << "Unable to open " << studentsFile << "\n";
return 1;
}
}
I tried using getline with 3 arguments and " " as the delimiter but that would also split the name part of the string, so I'm not sure how to do this another way.
Replace std::cin with your input file stream of course. It would be probably sane to "trim" the name result, unless you know by 100% the input is well formatted. I added only bare-minimal error state handling to somehow "survive".
Names are read also for single/three/more variants of course, as any real world application should.
#include <iostream>
#include <string>
#include <stdexcept>
int main()
{
std::string line, name;
unsigned long long regNo;
size_t nameOfs;
while (true) {
// Read full non-empty line from input stream
try {
std::getline(std::cin, line);
if (line.empty()) break;
}
catch(const std::ios_base::failure & readLineException) {
break;
}
// parse values:
// 1. unsigned long long ending with single white space as "regNo"
// 2. remaining part of string is "name"
try {
regNo = std::stoull(line, &nameOfs);
name = line.substr(nameOfs + 1);
}
catch(const std::logic_error & regNoException) {
// in case of invalid input format, just stop processing
std::cout << "Invalid regNo or name in line: [" << line << "]";
break;
}
// here values regNo + name are parsed -> insert them into some vector/etc.
std::cout << "RegNo [" << regNo << "] name [" << name << "]\n";
}
}
A regular expression could be used:
We can then select group 2 and 3 from the result.
std::vector<Student> students;
std::regex r{R"(((\d+) )(.+))"};
for(std::string line; getline(students_stream, line);) {
auto it = std::sregex_iterator(line.begin(), line.end(), r);
auto end = std::sregex_iterator();
if(it == end || it->size() != 4)
throw std::runtime_error("Could not parse line containing the following text: " + line);
for(; it != end; ++it) {
auto match = *it;
auto regNo_text = match[2].str();
auto regNo{std::stoi(regNo_text)};
auto name = match[3].str();
students.emplace_back(name, regNo);
}
}
Live demo
You can take input using getline()and read one complete line(no third argument) and then use stringstream to extract the number and the remaining string. Example of stringstream:
string s = "30304 Homer Simpson", name;
stringstream ss(s);
int num;
ss >> num; //num = 30304
getline(ss, name); //name = Homer Simpson
cout << num;
cout << name;
I am looking for a way to extract out data from a txt file which data is seperated by row and each column of data is seperaed by |
Here's an example
12|john bravo|123 kings street
15|marry jane|321 kings street
Previously i did it by separating using spaces like this
12 john kingstreet
15 marry kingstreet
But it poses a problem when I add a last name to the names/ add an address with spaces, ex: john bravo
So I decided to separate the column data using |
this is how I extract the data
struct PERSON{
int id;
string name;
string address;
};
//extract
int main(){
PERSON data[2];
ifstream uFile("people.txt");
int i = 0;
while(uFile >> data[i].id >> data[i].name >> data[i].address){
i++;
}
return 0;
}
So how do i extract if the columns are separated by | ??
Use getline() twice:
First, get each line use default seperator (new line); second, for each segment from first step, use '|' as seperator. "stringstream" class may be used to transfer data.
#edward The code below is modified from yours, and I think #P0W58 's answer is better.
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
const int length = 2;
struct PERSON
{
int id;
string name;
string address;
};
//extract
int main()
{
PERSON data[length];
ifstream fin("people.txt");
int i = 0;
while(true)
{
string segment;
if (!getline(fin, segment))
break;
stringstream transporter;
transporter << segment;
string idString;
getline(transporter, idString, '|');
getline(transporter, data[i].name, '|');
getline(transporter, data[i].address, '|');
stringstream idStream;
idStream << idString;
idStream >> data[i].id;
i++;
}
for (i=0; i<length; i++)
cout << data[i].id << '+' << data[i].name << '+'\
<< data[i].address << endl;
return 0;
}
To read into a struct , I'd overload << and then parse the text as mentioned in one of the answer .
Something like this :
#include<sstream>
//...
struct PERSON{
int id;
std::string name;
std::string address;
friend std::istream& operator >>(std::istream& is, PERSON& p)
{
std::string s;
std::getline(is, s); //Read Line, use '\r' if your file is saved on linux
std::stringstream ss(s);
std::getline(ss, s, '|'); //id
p.id = std::atoi(s.c_str());
std::getline(ss, p.name, '|'); // name
std::getline(ss, p.address, '|'); //address
return is ;
}
};
And then you can probably do,
std::ifstream fin("input.txt");
PERSON p1;
while (fin >> p1)
//std::cout << p1.id << p1.name << std::endl ;
You can overload << too
Use boost::tokenizer or find first of like :
// code example
string s = "12|john bravo|123 kings street";
string delimiters = "|";
size_t current;
size_t next = -1;
do
{
current = next + 1;
next = s.find_first_of( delimiters, current );
cout << s.substr( current, next - current ) << endl;
}
while (next != string::npos);
I am using the following code for splitting of each word into a Token per line. My problem lies here: I want a continuous update on my number of tokens in the file. The contents of the file are:
Student details:
Highlander 141A Section-A.
Single 450988012 SA
Program:
#include <iostream>
using std::cout;
using std::endl;
#include <fstream>
using std::ifstream;
#include <cstring>
const int MAX_CHARS_PER_LINE = 512;
const int MAX_TOKENS_PER_LINE = 20;
const char* const DELIMITER = " ";
int main()
{
// create a file-reading object
ifstream fin;
fin.open("data.txt"); // open a file
if (!fin.good())
return 1; // exit if file not found
// read each line of the file
while (!fin.eof())
{
// read an entire line into memory
char buf[MAX_CHARS_PER_LINE];
fin.getline(buf, MAX_CHARS_PER_LINE);
// parse the line into blank-delimited tokens
int n = 0; // a for-loop index
// array to store memory addresses of the tokens in buf
const char* token[MAX_TOKENS_PER_LINE] = {}; // initialize to 0
// parse the line
token[0] = strtok(buf, DELIMITER); // first token
if (token[0]) // zero if line is blank
{
for (n = 1; n < MAX_TOKENS_PER_LINE; n++)
{
token[n] = strtok(0, DELIMITER); // subsequent tokens
if (!token[n]) break; // no more tokens
}
}
// process (print) the tokens
for (int i = 0; i < n; i++) // n = #of tokens
cout << "Token[" << i << "] = " << token[i] << endl;
cout << endl;
}
}
Output:
Token[0] = Student
Token[1] = details:
Token[0] = Highlander
Token[1] = 141A
Token[2] = Section-A.
Token[0] = Single
Token[1] = 450988012
Token[2] = SA
Expected:
Token[0] = Student
Token[1] = details:
Token[2] = Highlander
Token[3] = 141A
Token[4] = Section-A.
Token[5] = Single
Token[6] = 450988012
Token[7] = SA
So I want it to be incremented so that I could easily identify the value by its variable name. Thanks in advance...
What's wrong with the standard, idiomatic solution:
std::string line;
while ( std::getline( fin, line ) ) {
std::istringstream parser( line );
int i = 0;
std::string token;
while ( parser >> token ) {
std::cout << "Token[" << i << "] = " << token << std::endl;
++ i;
}
}
Obviously, in real life, you'll want to do more than just
output each token, and you'll want more complicated parsing.
But anytime you're doing line oriented input, the above is the
model you should be using (probably keeping track of the line
number as well, for error messages).
It's probably worth pointing out that in this case, an even
better solution would be to use boost::split in the outer
loop, to get a vector of tokens.
I would just let iostream do the splitting
std::vector<std::string> token;
std::string s;
while (fin >> s)
token.push_back(s);
Then you can output the whole array at once with proper indexes.
for (int i = 0; i < token.size(); ++i)
cout << "Token[" << i << "] = " << token[i] << endl;
Update:
You can even omit the vector altogether and output the tokens as you read them from the input strieam
std::string s;
for (int i = 0; fin >> s; ++i)
std::cout << "Token[" << i << "] = " << token[i] << std::endl;