Issues reading text from a file. Getting double reads - c++

Hello friends at stackoverflow!
I have written a program that saves 3 strings to a textfile. The code to write is:
void my_class::save_file(const char* text) {
for(auto ad: ad_list) {
std::ofstream outputFile;
outputFile.open(text, std::ios::app);
if (outputFile.is_open()) {
outputFile << ad.string1 << "|" << ad.string2 << "|" << ad.string3 << "|" << std::endl;
outputFile.close();
}
}
}
This gives me the file with the content:
string1|string2|string3|
<- //extra line here
When i read from this file I do it with the following function:
void my_class::read_file(const char* text) {
// read file to stringsteam
std::stringstream ss;
std::ifstream fp (text);
if (fp.is_open()) {
ss << fp.rdbuf();
}
fp.close();
string file_contents = ss.str();
// split to lines
auto lines = splitString(file_contents, "\n");
// split to parts
for (auto ad_text: lines) { // <-- this is the possibly the issue
auto ad_parts = splitString(ad_text, "|");
string string1 = ad_parts[0];
string string2 = ad_parts[1];
string string3 = ad_parts[2];
auto new_class = MyClass(string1, string2, string3);
//Adds (push_back) the class to its vector
add_class(new_class);
}
}
vector<string> my_class::splitString(string text, string delimiter) {
vector<string> parts;
size_t start = 0;
size_t end = 0;
while((end = text.find(delimiter, start)) != std::string::npos) {
size_t length = end - start;
parts.push_back(text.substr(start, length));
start = end + delimiter.length();
}
start = end + delimiter.length();
size_t length = text.length() - start;
parts.push_back(text.substr(start, length));
return parts;
}
Result:
string1|string2|string3|
string1|string2|string3|
I'm pretty sure this copy is a result of the new line left behind by the write function. The read function splits the text into lines, and that would be 2 lines.
I am currently splitting the lines in this loop "for (auto ad_text: lines)", and what I want to do is basically ( lines - 1 ). I do not understand how I can do that with the way I am interating through the for loop.
Thanks in advance!

You can simplify your splitString function to look like below. Note the second parameter to splitString is a char now and not a std::string.
//note the second parameter is a char and not a string now
vector<string> splitString(string text, char delimiter) {
vector<string> parts;
std::string words;
std::istringstream ss(text);
while(std::getline(ss, words,delimiter ))
{
parts.push_back(words);
}
return parts;
}
For the above modification to work you have to make 2 additional changes:
Change 1: Replace auto lines = splitString(file_contents, "\n"); to:
auto lines = splitString(file_contents, '\n');
Change 2: Replace auto ad_parts = splitString(ad_text, "|"); to:
auto ad_parts = splitString(ad_text, '|');

Related

How to add spaces into a text file?

I have a text file like this:
100,Nguyen Van A,2004
101,Tran Thi B,2004
102,Vo Van C,2005
103,Truong Thi D,2005
How can I add one blank space right after each "," into that file using C++?
I've tried using find() function but nothing worked.
svFile.open("list_student.txt");
while (getline(svFile, line, ','))
{
if (line.find(",", 0) != string::npos)
svFile << " ";
}
Two options, read and write the file character by character, or option 2, read the entire text file into a string and then perform your required changes and write the file back:
Option 1 (Character by Character):
char ch;
fstream fin("list_student.txt", fstream::in);
fstream fout("list_student_result.txt", fstream::out);
while (fin >> noskipws >> ch) {
fout << ch;
if (ch==',')
{
fout << ' ';
}
}
fout.close();
Option 2 (Read/Write entire file):
#include <iostream>
#include <fstream>
int main()
{
// Read the entire file
std::ifstream t("list_student.txt");
std::stringstream buffer;
buffer << t.rdbuf();
std::string myString = buffer.str();
size_t start = 0;
int pos = 0;
// Set your start character and replace with string here
std::string comma(",");
std::string replaceWith(", ");
// Replace the comma "," with ", " (and a space)
while ((pos = myString.find(comma, pos)) != std::string::npos) {
myString.replace(pos, comma.length(), replaceWith);
pos += replaceWith.length();
}
// Write the formatted text back to a file
std::ofstream out("list_student_result.txt");
out << myString;
out.close();
return 0;
}

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.

Extract and set variables from input file c++

So I want to get a specific part from every line of the input file.
So far I got this:
ifstream fin("text.txt");
string line;
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
istringstream sin1(line.substr(line.find("path1=") + 1));
sin1 >> path1;
istringstream sin2(line.substr(line.find("path2=") + 1));
sin2 >> path2;
}
}
From what I understood the (line.substr(line.find("VAR_CAL_PATH2=") + 1)) part will take whatever it's after "path1=" and will put it into path1, I guess I got it wrong. My input file has two lines:
Set path1="somepath"
Set path2="someotherpath"
When the while loop ends I get path1="Set" and path2="someotherpath" but what I want is path1="somepath" and path2="someotherpath"
As #Jordfräs says, find() returns the position of the start of the string.
When you try to parse the path2 value, you overwrite the value of path1.
The code with these fixes:
const string path1field = "path1=";
const string path2field = "path2=";
string path1 = "", path2 = "";
ifstream fin("text.txt");
string line;
while (getline(fin, line))
{
if (line.find("Set") != string::npos)
{
size_t path1pos = line.find(path1field);
size_t path2pos = line.find(path2field);
if (path1pos != string::npos)
{
istringstream sin1(line.substr(path1pos + path1field.length()));
sin1 >> path1;
}
if (path2pos != string::npos)
{
istringstream sin2(line.substr(path2pos + path2field.length()));
sin2 >> path2;
}
}
}
cout << "path1: " << path1 << endl;
cout << "path2: " << path2 << endl;
The find() function in std::string returns the position of the start of the string. Adding 1 will point to the start + 1, not the position after the string you search for.
There is plenty of good documentation of the standard library available, e.g., http://en.cppreference.com/w/cpp/string/basic_string/find
Another (more general) solution for string variables could be:
ifstream fin("text.txt");
string line;
const vector<string> pathsNames={"path1=", "path2="};
vector<string> paths(pathsNames.size());
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
if(line.find(pathsNames[x])!=string::npos){
paths[x]=line.substr(line.find(pathsNames[x]) + pathsNames[x].length());
break;
}
}
}
}
//print results
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
cout << pathsNames[x] << paths[x] << endl;
}

C++ Read file line by line then split each line using the delimiter

i have searched and got a good grasp of the solution from a previous post but I am still stuck a bit.
My problem is instead of "randomstring TAB number TAB number NL"
My data is "number (space colon space) number (space colon space) sentence"
I've edited the code below but still can't get it to work 100% because the parameters getline takes is (stream, string, delimiter).
For some reason, it only gets the first word of the sentence as well and not the rest.
Previous post
I want to read a txt file line by line and after reading each line, I want to split the line according to the tab "\t" and add each part to an element in a struct.
my struct is 1*char and 2*int
struct myStruct
{
char chr;
int v1;
int v2;
}
where chr can contain more than one character.
A line should be something like:
randomstring TAB number TAB number NL
SOLUTION
std::ifstream file("plop");
std::string line;
while(std::getline(file, line))
{
std::stringstream linestream(line);
std::string data;
int val1;
int val2;
// If you have truly tab delimited data use getline() with third parameter.
// If your data is just white space separated data
// then the operator >> will do (it reads a space separated word into a string).
std::getline(linestream, data, '\t'); // read up-to the first tab (discard tab).
// Read the integers using the operator >>
linestream >> val1 >> val2;
}
At the following code line, the data variable will hold the complete line. And linestream will be consumed, so further readings will not yield anything.
std::getline(linestream, data, '\t'); // read up-to the first tab (discard tab).
Instead, you can just work on the line like this
while (std::getline(file, line))
{
int token1 = std::stoi(line.substr(0, line.find(" : ")));
line.erase(0, line.find(" : ") + 3);
int token2 = std::stoi(line.substr(0, line.find(" : ")));
line.erase(0, line.find(" : ") + 3);
std::string token3 = line;
}
What exactly is your problem ?
//Title of this code
//clang 3.4
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <vector>
struct Data
{
int n1;
int n2;
std::string sequence;
};
std::ostream& operator<<(std::ostream& ostr, const Data& data)
{
ostr << "{" << data.n1 << "," << data.n2 << ",\"" << data.sequence << "\"}";
return ostr;
}
std::string& ltrim(std::string& s, const char* t = " \t")
{
s.erase(0, s.find_first_not_of(t));
return s;
}
std::string& rtrim(std::string& s, const char* t = " \t")
{
s.erase(s.find_last_not_of(t) + 1);
return s;
}
std::string& trim(std::string& s, const char* t = " \t")
{
return ltrim(rtrim(s, t), t);
}
int main()
{
std::string file_content{
"1\t1\t\n"
"2\t2\tsecond sequence\t\n"
"3\t3\tthird sequence\n"};
std::istringstream file_stream{file_content};
std::string line;
std::vector<Data> content;
while(std::getline(file_stream, line))
{
std::istringstream line_stream{line};
Data data{};
if(!(line_stream >> data.n1 >> data.n2))
{
std::cout << "Failed to parse line (numbers):\n" << line << "\n";
break;
}
auto numbers_end = line_stream.tellg();
if(numbers_end == -1)
{
std::cout << "Failed to parse line (sequence):\n" << line << "\n";
break;
}
data.sequence = line.substr(numbers_end);
trim(data.sequence);
content.push_back(std::move(data));
}
std::copy(content.cbegin(), content.cend(),
std::ostream_iterator<Data>(std::cout, "\n"));
}
Live
Live with colons

How to print "justified" text in the console using modern C++

How can I format text "justified" so it aligns to the left and right side for a given width?
int main()
{
printJustified("A long text with many words. "
"A long text with many words. "
"A long text with many words. "
"A long text with many words. "
"A long text with many words.");
}
Expected output:
A long text with many words. A long text with
many words. A long text with many words. A
long text with many words.
A simple example how to solve this problem is this:
#include <iostream>
#include <sstream>
#include <list>
const int pageWidth = 78;
typedef std::list<std::string> WordList;
WordList splitTextIntoWords( const std::string &text )
{
WordList words;
std::istringstream in(text);
std::copy(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>(),
std::back_inserter(words));
return words;
}
void justifyLine( std::string line )
{
size_t pos = line.find_first_of(' ');
if (pos != std::string::npos) {
while (line.size() < pageWidth) {
pos = line.find_first_not_of(' ', pos);
line.insert(pos, " ");
pos = line.find_first_of(' ', pos+1);
if (pos == std::string::npos) {
pos = line.find_first_of(' ');
}
}
}
std::cout << line << std::endl;
}
void justifyText( const std::string &text )
{
WordList words = splitTextIntoWords(text);
std::string line;
for (const std::string& word : words) {
if (line.size() + word.size() + 1 > pageWidth) { // next word doesn't fit into the line.
justifyLine(line);
line.clear();
line = word;
} else {
if (!line.empty()) {
line.append(" ");
}
line.append(word);
}
}
std::cout << line << std::endl;
}
int main()
{
justifyText("This small code sample will format a paragraph which "
"is passed to the justify text function to fill the "
"selected page with and insert breaks where necessary. "
"It is working like the justify formatting in text "
"processors.");
return 0;
}
It works like this:
First the text is split into words.
The words are added to lines until the line can not take more words.
For each line, space are added between the words until the line matches the requested width.