How do I extract an integer in a string? - c++

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.

Related

How to find a word which contains digits in a string

I need to check words inside the string to see whether any of them contains digits, and if it isn't — erase this word. Then print out the modified string
Here's my strugle to resolve the problem, but it doesn't work as I need it to
void sentence_without_latin_character( std::string &s ) {
std::cout << std::endl;
std::istringstream is (s);
std::string word;
std::vector<std::string> words_with_other_characters;
while (is >> word) {
std::string::size_type temp_size = word.find(std::ctype_base::digit);
if (temp_size == std::string::npos) {
word.erase(word.begin(), word.begin() + temp_size);
}
words_with_other_characters.push_back(word);
}
for (const auto i: words_with_other_characters) {
std::cout << i << " ";
}
std::cout << std::endl;
}
This part is not doing what you think it does:
word.find(std::ctype_base::digit);
std::string::find only searches for complete substrings (or single characters).
If you want to search for a set of some characters in a string, use std::string::find_first_of instead.
Another option is testing each character using something like std::isdigit, possibly with an algorithm like std::any_of or with a simple loop.
As Acorn explained, word.find(std::ctype_base::digit) does not search for the first digit. std::ctype_base::digit is a constant that indicates a digit to specific std::ctype methods. In fact there's a std::ctype method called scan_is that you can use for this purpose.
void sentence_without_latin_character( std::string &s ) {
std::istringstream is (s);
std::string word;
s.clear();
auto& ctype = std::use_facet<std::ctype<char>>(std::locale("en_US.utf8"));
while (is >> word) {
auto p = ctype.scan_is(std::ctype_base::digit, word.data(), &word.back()+1);
if (p == &word.back()+1) {
s += word;
if (is.peek() == ' ') s += ' ';
}
}
std::cout << s << std::endl;
}

How to seperate string by specific delimiter?

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

Reverse word order in sentence

I'm having difficulty creating a function that reverse the order of the sentence around. I've read many functions on how to recursively reverse the letters around and I have successfully done so, but I do not want to reverse the letters in the words. I want to reverse the placement of the words in the sentence.
Example would be:
This is a sentence.
sentence. a is This
This is my code so far. How do I go from reversing order of letters of the entire sentence to placement order of words in a sentence?
The output of the current code would provide: !dlroW olleH
void reverse(const std::string str)
{
int length = str.size();
if(length > 0)
{
reverse(str.substr(0,length-1));
std::cout << str[0];
}
}
Edit: Additional question. If this was a char array would the logic be different?
Simplify your logic by using a std::istringstream and a helper function. The program below works for me.
#include <sstream>
#include <iostream>
void reverse(std::istringstream& stream)
{
std::string word;
if ( stream >> word )
{
reverse(stream);
std::cout << word << " ";
}
}
void reverse(const std::string str)
{
std::istringstream stream(str);
reverse(stream);
std::cout << std::endl;
}
int main(int argc, char** argv)
{
reverse(argv[1]);
return 0;
}
// Pass string which comes after space
// reverse("This is a sentence.")
// reverse("is a sentence.")
// reverse("a sentence.")
// reverse("sentence.")
// will not find space
// start print only word in that function
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos == string::npos) // exit condition
{
string str1 = str.substr(0, pos);
cout << str1.c_str() << " " ;
return;
}
reverse(str.substr(pos+1));
cout << str.substr(0, pos).c_str() << " ";
}
Simple to understand:
void reverse(const std::string str)
{
int pos = str.find_first_of(" ");
if (pos != string::npos) // exit condition
{
reverse(str.substr(pos + 1));
}
cout << str.substr(0, pos).c_str() << " ";
}
std::vector<std::string> splitString(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> tokens;
while (getline(ss, item, delim)) {
tokens.push_back(item);
}
return tokens;
}
void reverseString(const std::string& string) {
std::vector<std::string> words = splitString(string, ' ');
auto end = words.rend();
for (auto it = words.rbegin(); it <= end; it++) {
std::cout << *it << std::endl;
}
}
reverseString("This is a sentence.");
You can split input and print them in inverse order
Or if you want to use recursive structure just move the cout after calling a function like this:
void reverse(const std::string str)
{
std::stringstream ss(str);
std::string firstWord, rest;
if(ss >> firstWord)
{
getline(ss , rest);
reverse(rest);
std::cout << firstWord << " ";
}
}
I am not a C++ programmer, but I would create another array (tempWord[ ]) to store individual word.
Scan each word and store them into tempWord array. In your case, the words are separated by space, so:
a.get the index of the next space,
b substring to the index of the next space and
c. you should get {"This", "is", "a", "sentence."}
Add them up again reversely:
a. loop index i from "tempWord.length -1" to "0"
b. new String = tempWord[i]+" ";
print out result.

Splitting input string c++ [duplicate]

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;

reading from istringstream using getline and maybe strtok somehow?

I want to read data from stream, which has specific format, such as:
"number:name_that_can_contain_spaces:string,string,string..." without quotes where ... means that I dont know how many strings are there separated with commas and strings can have spaces before and after it but not in the middle of string, I want to stop reading at new line
I only come up with using getline() and store each line into string, but I dont know how to continue, if there is something like strtok(line, ":",":",",","\n") which would parse it for me or I have to parse it myself character by character
example of valid line format is:
54485965:abc abc abc: some string, next string , third string\n
parsed result would be:
int 54485965
string "abc abc abc"
string "some string"
string "next string"
string "third string"
You can read line with std::getline and then split it with std::string::find and std::string::substr. In the code below we read line from file data, then find : (so everything before it becomes number which we parse into int with std::stoi) and throw away first part. Similar we do it with name. And in the end we fill std::list with strings separated by ,.
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <exception>
#include <stdexcept>
struct entry {
std::string name;
int number;
std::list<std::string> others;
};
int main(int argc, char** argv) {
std::ifstream input("data");
std::list<entry> list;
std::string line;
while(std::getline(input, line)) {
entry e;
std::string::size_type i = 0;
/* get number from line */
i = line.find(":");
if(i != std::string::npos) {
e.number = stoi(line.substr(0, i));
line = line.substr(i + 1);
} else {
throw std::runtime_error("error reading file");
}
/* get name from line */
i = line.find(":");
if(i != std::string::npos) {
e.name = line.substr(0, i);
line = line.substr(i + 1);
} else {
throw std::runtime_error("error reading file");
}
/* get other strings */
do {
i = line.find(",");
e.others.push_back(line.substr(0, i));
line = line.substr(i + 1);
} while(i != std::string::npos);
list.push_back(e);
}
/* output data */
for(entry& e : list) {
std::cout << "name: " << e.name << std::endl;
std::cout << "number: " << e.number << std::endl;
std::cout << "others: ";
for(std::string& s : e.others) {
std::cout << s << ",";
}
std::cout << std::endl;
}
return 0;
}