Read a string line by line using c++ - c++

I have a std::string with multiple lines and I need to read it line by line.
Please show me how to do it with a small example.
Ex: I have a string string h;
h will be:
Hello there.
How are you today?
I am fine, thank you.
I need to extract Hello there., How are you today?, and I am fine, thank you. somehow.

#include <sstream>
#include <iostream>
int main() {
std::istringstream f("line1\nline2\nline3");
std::string line;
while (std::getline(f, line)) {
std::cout << line << std::endl;
}
}

There are several ways to do that.
You can use std::string::find in a loop for '\n' characters and substr() between the positions.
You can use std::istringstream and std::getline( istr, line ) (Probably the easiest)
You can use boost::tokenize

this would help you :
http://www.cplusplus.com/reference/iostream/istream/getline/

If you'd rather not use streams:
int main() {
string out = "line1\nline2\nline3";
size_t start = 0;
size_t end;
while (1) {
string this_line;
if ((end = out.find("\n", start)) == string::npos) {
if (!(this_line = out.substr(start)).empty()) {
printf("%s\n", this_line.c_str());
}
break;
}
this_line = out.substr(start, end - start);
printf("%s\n", this_line.c_str());
start = end + 1;
}
}

I was looking for some standard implementation for a function which can return a particular line from a string. I came across this question and the accepted answer is very useful. I also have my own implementation which I would like to share:
// CODE: A
std::string getLine(const std::string& str, int line)
{
size_t pos = 0;
if (line < 0)
return std::string();
while ((line-- > 0) and (pos < str.length()))
pos = str.find("\n", pos) + 1;
if (pos >= str.length())
return std::string();
size_t end = str.find("\n", pos);
return str.substr(pos, (end == std::string::npos ? std::string::npos : (end - pos + 1)));
}
But I have replaced my own implementation with the one shown in the accepted answer as it uses standard function and would be less bug-prone..
// CODE: B
std::string getLine(const std::string& str, int lineNo)
{
std::string line;
std::istringstream stream(str);
while (lineNo-- >= 0)
std::getline(stream, line);
return line;
}
There is behavioral difference between the two implementations. CODE: B removes the newline from each line it returns. CODE: A doesn't remove newline.
My intention of posting my answer to this not-active question is to make others see possible implementations.
NOTE:
I didn't want any kind of optimization and wanted to perform a task given to me in a Hackathon!

Related

Function to separate each word from a string and put them into a vector, without using auto keyword?

I'm really stuck here. So I can't edit the main function, and inside it there is a function call with the only parameter being the string. How can I make this function put each word from the string into a vector, without using the auto keyword? I realize that this code is probably really wrong but its my best attempt at what it should look like.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
vector<string> extract_words(const char * sentence[])
{
string word = "";
vector<string> list;
for (int i = 0; i < sentence.size(); ++i)
{
while (sentence[i] != ' ')
{
word = word + sentence[i];
}
list.push_back(word);
}
}
int main()
{
sentence = "Help me please" /*In the actual code a function call is here that gets input sentence.*/
if (sentence.length() > 0)
{
words = extract_words(sentence);
}
}
Do you know how to read "words" from std::cin?
Then you can put that string in a std::istringstream which works like std::cin but for "reading" strings instead.
Use the stream extract operator >> in a loop to get all the words one by one, and add them to the vector.
Perhaps something like:
std::vector<std::string> get_all_words(std::string const& string)
{
std::vector<std::string> words;
std::istringstream in(string);
std::string word;
while (in >> word)
{
words.push_back(word);
}
return words;
}
With a little more knowledge of C++ and its standard classes and functions, you can actually make the function a lot shorter:
std::vector<std::string> get_all_words(std::string const& string)
{
std::istringstream in(string);
return std::vector<std::string>(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>());
}
I recommend making the argument to the function a const std::string& instead of const char * sentence[]. A std::string has many member functions, like find_first_of, find_first_not_of and substr and more that could help a lot.
Here's an example using those mentioned:
std::vector<std::string> extract_words(const std::string& sentence)
{
/* Control char's, "whitespaces", that we don't want in our words:
\a audible bell
\b backspace
\f form feed
\n line feed
\r carriage return
\t horizontal tab
\v vertical tab
*/
static const char whitespaces[] = " \t\n\r\a\b\f\v";
std::vector<std::string> list;
std::size_t begin = 0;
while(true)
{
// Skip whitespaces by finding the first non-whitespace, starting at
// "begin":
begin = sentence.find_first_not_of(whitespaces, begin);
// If no non-whitespace char was found, break out:
if(begin == std::string::npos) break;
// Search for a whitespace starting at "begin + 1":
std::size_t end = sentence.find_first_of(whitespaces, begin + 1);
// Store the result by creating a substring from "begin" with the
// length "end - begin":
list.push_back(sentence.substr(begin, end - begin));
// If no whitespace was found, break out:
if(end == std::string::npos) break;
// Set "begin" to the char after the found whitespace before the loop
// makes another lap:
begin = end + 1;
}
return list;
}
Demo
With the added restriction "no breaks", this could be a variant. It does exactly the same as the above, but without using break:
std::vector<std::string> extract_words(const std::string& sentence)
{
static const char whitespaces[] = " \t\n\r\a\b\f\v";
std::vector<std::string> list;
std::size_t begin = 0;
bool loop = true;
while(loop)
{
begin = sentence.find_first_not_of(whitespaces, begin);
if(begin == std::string::npos) {
loop = false;
} else {
std::size_t end = sentence.find_first_of(whitespaces, begin + 1);
list.push_back(sentence.substr(begin, end - begin));
if(end == std::string::npos) {
loop = false;
} else {
begin = end + 1;
}
}
}
return list;
}

How to break up a string into a vector fast?

I am processing CSV and using the following code to process a single line.
play with code
std::vector<std::string> string_to_vector(const std::string& s, const char delimiter, const char escape) {
std::stringstream sstr{s};
std::vector<std::string> result;
while (sstr.good()) {
std::string substr;
getline(sstr, substr, delimiter);
while (substr.back() == escape) {
std::string tmp;
getline(sstr, tmp, delimiter);
substr += "," + tmp;
}
result.emplace_back(substr);
}
return result;
}
What it does: Function breaks up string s based on delimiter. If the delimiter is escaped with escape the delimiter will be ignored.
This code works but is super slow. How can I speed it up?
Do you know any existing csv processing implementation that does exactly this and which I could use?
The fastest way to do something is to not do it at all.
If you can ensure that your source string s will outlive the use of the returned vector, you could replace your std::vector<std::string> with std::vector<char*> which would point to the beginning of each substring. You then replace your identified delimiters with zeroes.
[EDIT] I have not moved up to C++17, so no string_view for me :)
NOTE: typical CSV is different from what you imply; it doesn't use escape for the comma, but surrounds entries with comma in it with double quotes. But I assume you know your data.
Implementation:
#include <iostream>
#include <vector>
#include <string>
std::vector<char*> string_to_vector(std::string& s,
const char delimiter, const char escape)
{
size_t prev(0), pos(0), from(0);
std::vector<char*> v;
while ((pos = s.find(delimiter, from)) != s.npos)
{
if (pos == 0 || s[pos - 1] != escape)
{
s[pos] = 0;
v.push_back(&s[prev]);
prev = pos + 1;
}
from = pos + 1;
}
v.push_back(&s[prev]);
return v;
}
int main() {
std::string test("this,is,a\\,test");
std::vector<char*> v = string_to_vector(test, ',', '\\');
for (auto& s : v)
std::cout << s << " ";
}

Store .txt file into a char* 2d Vector C++

I know there are lots of questions with similar titles here, but no one seems to work for me.
I have this kind of txt file:
tree pine
color blue
food pizza
and I want to store the items in a char* 2d vector, such as
vector<vector<char*>> data;
..
..
data[0][0] = tree
data[0][1] = pine
data[1][1] = blue
ecc
This is the code:
// parse configuration file
bool Configuration::fileParser(char* filename)
{
vector<vector<char*>> data;
fstream fin("data/setup.txt");
string line;
while (fin && getline(fin, line))
{
vector<char*> confLine;
char* word = NULL;
stringstream ss(line);
while (ss && ss >> word)
{
confLine.push_back(word);
}
data.push_back(confLine);
}
storeData(data);
return 0;
}
But when I run the code an exception is thrown.
Exception thrown: write access violation.
How can I solve this problem?
Thank you
You haven't allocated any memory into which the data can be written. You'd need something like char* word = new char[50];. But just use a std::string it is safer and easier.
Disclaimer: I do not have a compiler on hand to test the following code with files, but it should work.
Here is a reference I used: Parse (split) a string in C++ using string delimiter (standard C++)
Discription: Basically the following code parses the passed in file line by line then assigns the first word and second word into the vector. Notice that I used string(s) in the example because I didn't want to think about memory management.
#pragma once
#include <vector>
#include <fstream>
#include <string>
void Configuration::fileParser(string fileName)
{
vector<vector<string>> data;
ifstream configFile(fileName);
string line, token;
string delimiter = " ";
size_t pos;
if (configFile.is_open())
{
int n = 0;
while (getline(configFile, line))
{
if (!line || line == "")
break; //added as a safety measure
pos = 0;
if ((pos = line.find(delimiter)) != string::npos)
{
token = line.substr(0, pos);
data[n][0] = token; //add first word to vector
line.erase(0, pos + delimiter.length());
}
if ((pos = line.find(delimiter)) != string::npos)
{
token = line.substr(0, pos);
data[n][1] = token; //add second word to vector
line.erase(0, pos + delimiter.length());
}
n++;
}
}
storeData(data);
}

Program gets "Expression: string subscript out of range"

#include <iostream>
#include <string>
using namespace std;
string Latin(string words)
{
string strWord, strSentence = "";
int length = 0, index = 0;
while (words[index] != '\0')
{
if(words.find(' ', index) != -1)
{
length = words.find(' ', index);
length -= index;
strWord = words.substr(index,length);
strWord.insert(length, "ay");
strWord.insert(length, 1, words[index]);
strWord.erase(0,1);
index += length +1;
}
else
{
strWord = words.substr(index);
length = strWord.length();
strWord.insert(length, "ay");
strWord.insert(length,1,words[index]);
strWord.erase(0,1);
index = words.length();
}
strSentence += (strWord + " ");
}
return strSentence;
}
int main()
{
string str;
getline(cin,str);
str = Latin(str);
cout<<str<<endl;
return 0;
}
I get this error that says
I have no clue what to do. As I am new to this, this is a program that is suppose to ask for user input of a length of words and translate them into pig Latin. Any help would be greatly appreciated.
Unless I really wanted to make my own life difficult, I'd do this quite a bit differently. First, I'd use a std::stringstream to break the input string into words to process. Then, I'd use std::rotate to move the first character of the string to the end. Finally, I'd wrap that all in std::transform to manage applying the function to each word in succession.
std::string line;
std::getline(std::cin, line);
std::stringstream buffer(line);
std::stringstream result;
std::transform(std::istream_iterator<std::string>(buffer),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(result, " "),
[](std::string s) {
std::rotate(s.begin(), s.begin() + 1, s.end());
s += "ay";
return s;
});
Of course, this doesn't know the special rules for things like words that start with vowels or letter pairs like sh or ch, but it looks like that's outside the scope of the task at hand.
For more on std::rotate, I recommend watching some of Sean Parent's videos.

set<string>: how to list not strings starting with given string and ending with `/`?

for example we have in our set:
bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog
bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog
bin/obj/Debug/vc100.idb
bin/obj/Debug/vc100.pdb
So this is what I tried based on this grate answer:
#include <iostream>
#include <algorithm>
#include <set>
#include <string>
#include <iterator>
using namespace std;
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
};
void directory_listning_without_directories_demo()
{
set<string> output;
set<string> demo_set;
demo_set.insert("file1");
demo_set.insert("file2");
demo_set.insert("folder/file1");
demo_set.insert("folder/file2");
demo_set.insert("folder/folder/file1");
demo_set.insert("folder/folder/file2");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/ra.write.1.tlog");
demo_set.insert("bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog");
demo_set.insert("bin/obj/Debug/vc100.idb");
demo_set.insert("bin/obj/Debug/vc100.pdb");
std::transform(demo_set.begin(),
demo_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug/"));
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
int main()
{
directory_listning_without_directories_demo();
cin.get();
return 0;
}
This outputs:
CloudServerPrototype/
file1
file2
folder/
folder/folder/
vc100.idb
vc100.pdb
and we are given with bin/obj/Debug/string. We want to cout:
vc100.idb
vc100.pdb
CloudServerPrototype/
How to do such thing?
Quick example of what you want to do.
String.find(): http://www.cplusplus.com/reference/string/string/find/
String.subStr(): http://www.cplusplus.com/reference/string/string/substr/
string str = "bin/obj/Debug/vc100.pdb";
string checkString ("bin/obj/Debug");
// Check if string starts with the check string
if (str.find(checkString) == 0){
// Check if last letter if a "/"
if(str.substr(str.length()-1,1) == "/"){
// Output strating at the end of the check string and for
// the differnce in the strings.
cout << str.substr(checkString.length(), (str.length() - checkString.length()) ) << endl;
}
}
It's not clear with which part of the problem you are stuck, so here is a starter for you.
To get the parts of the strings between "given string" and the final '/' (where present):
std::string get_pertinent_part(const std::string& s)
{
std::string::size_type first = 0;
if (s.find(given_string) == 0)
{
first = given_string.length() + 1;
}
std::string::size_type count = std::string::npos;
std::string::size_type pos = s.find_last_of("/");
if (pos != std::string::npos && pos > first)
{
count = pos + 1 - first;
}
return s.substr(first, count);
}
To insert these parts into a new set (output) to guarantee uniqueness you can use the following:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part);
You may wish to pass given_string into get_pertinent_part(), in which case you'll need to convert it to a functor:
struct get_pertinent_part
{
const std::string given_string;
get_pertinent_part(const std::string& s)
:given_string(s)
{
}
std::string operator()(const std::string& s)
{
std::string::size_type first = 0;
//
// ...same code as before...
//
return s.substr(first, count);
}
};
You can then call it this way:
std::transform(your_set.begin(),
your_set.end(),
std::inserter(output, output.end()),
get_pertinent_part("bin/obj/Debug"));
To output the new set:
std::copy(output.begin(),
output.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
Sorting the results is left as an exercise.
The easiest way I can think of, using the standard C functions, would be:
char * string1 = "bin/obj/Debug"
char * string2 = "bin/obj/Debug/CloudServerPrototype/rc.write.1.tlog"
char result[64];
// the above code is just to bring the strings into this example
char * position = strstr(string1, string2);
int substringLength;
if(position != NULL){
position += strlen(string2);
substringLength = strchr(position, '/') - position;
strncpy(result, position, substringLength);
}else{
strcpy(result, string1); // this case is for when your first string is not found
}
cout << result;
The first thing that occurs, is finding the substring, string1, in the string we are analyzing, being string2. Once we found the starting point, and assuming it was there at all, we add the length of that substring to that starting point using pointer arithmatic, and then find the resulting string's length by subtracting the starting position from the ending position, which is found with strchr(position, '/'). Then we simply copy that substring into a buffer and it's there to print with cout.
I am sure there is a fancy way of doing this with std::string, but I'll leave that to anyone who can better explain c++ strings, I never did manage to get comfortable with them, haha