This an adaptation of a program used earlier within another thread. The original code was designed to read individual words into an array of strings, and works fine.
This code is attempting to read the same words into a 2D array, maintaining the structure of the file. However, it doesn't work properly.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
usingnamespace std;
int main()
{
string testline;
string word[6][3];
ifstream Test ( "Data.txt" );
if (!Test)
{
cout << "There was an error opening the file.\n";
return 0;
}
//store words in array
int x=0,y=0;
while( Test>>testline )
{
word[y][x]=testline;
x++;
if (testline=="")
y++;
}
//output whole array with array position numbers for each entry
cout<<"Array contents:\n";
for (int y=0;y<6;y++)
{
for (int x=0;x<3;x++)
cout<<word[y][x]<<"("<<y<<","<<x<<")"<<endl;
}
return 0;
}
My input file:
Steven Seagal
1234 Post Drive
Ventura, CA 90734
Adam Sandler
356 Golf Street
Calabasas, CA 92136
The check on line 25 is failing to increment the vertical array counter when applicable. How can I get my >> operator to detect the end of a line? Also, how can I get the third position in each row to remain blank in the event the file only contains two strings on a line (such as their names)? My output looks like this:
How can I get my >> operator to detect the end of a line?
Use the right tool for the job. Use std::getline to extract a line, and break the string apart from there. You may wish to use a std::istringstream to help you parse the line.
Using a vector of vectors is a better idea than using a two dimensional array here. If an address looks like the following:
You could use getline to have each line stored in a string. Then use stringstreams to hold each line. Then once you are done processing a line put the '\n' into the array and increment.
Steven
Seagal
(newline)
1234
Post
Drive
(newline)
Ventura,
CA
90734
(newline)
(newline)
Adam
Sandler
(newline)
356
Golf
Street
(newline)
Calabasas,
CA
92136
(newline)
Edit: Looks like I had a similar idea, but cire beat me to it LOL.
This explains a lot about the program behavior, but I am fairly new to vectors and more comfortable with 2D arrays. Afterwards, I intended to adapt it to work with a 2D vector, as of course the fact that addresses may contain more than 3 fields is relevant. How do the &auto based loops work, I haven't seen that before?
I see that now, cire, and that's why I edited my comment. I'm still gaining familiarity with 2D vectors and how they work, though. I'm still curious about the &auto based loop.
int foobar[10] = {1,2,3,4,5,6,7,8,9,0};
for(auto& x : foobar)
{
cout << x << ',';
}
1,2,3,4,5,6,7,8,9,0,
'foobar' can be any STL container or normal array (or I believe any custom type... as long as your type provides begin() and end() methods).
'x' is a reference to each element in that container.
It's functionally the same as this:
1 2 3 4 5 6
for(int i = 0; i < 10; ++i)
{
auto& x = foobar[i];
cout << x << ',';
}
The only difference is:
- you don't need to explicitly state the size of the array/container in the for loop
- it works and has the same syntax regardless of the type of container
Interesting, I can see the potential uses for this. Thanks for the clarification. After adapting cire's earlier example, I cannot get the compiler to recognize emplace_back. How do you get your output and code windows side by side? This would make several of my posts easier to read through.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
usingnamespace std;
int main()
{
string line;
vector<vector<string> > lines;
ifstream Test ( "Data.txt" );
if (!Test)
{
cout << "There was an error opening the file.\n";
return 1;
}
else
{
//store words in vector
while(getline(Test,line));
{
vector<string> tokens;
string token;
while (Test>>token)
tokens.push_back(token);
if (tokens.size())
lines.emplace_back(move(tokens));
}
//output vector
for (auto& line : lines)
{
for (auto& token : line)
cout<<token<<" ";
cout<<endl;
}
return 0;
}
}
Line 31 - error: 'class std::vector<std::vector<std::basic_string<char> > >' has no member named 'emplace_back'
Line 31 - error: 'move' was not declared in this scope
Line 34 & 36 - error: range-based 'for' loops are not allowed in C++98 mode
Line 34 - error: ISO C++ forbids declaration of 'line' with no type [-fpermissive]
Line 36 - error: ISO C++ forbids declaration of 'token' with no type [-fpermissive]
Yet I've declared both line and token as strings.
I also received a warning in addition to the above errors:
Line 34 & 36 - warning: 'auto' changes meaning in C++11; please remove it [-Wc++0x-compat]
Just so you guys know, I'm using Code::Blocks 12.11.
Thanks, Disch! I missed that comment earlier. After going into the settings and changing that, it now compiles without errors, but I receive no output, and didn't know why at first. However, attempting to output lines[0][0] results in a program crash, so it's apparently not adding anything to the vector. I must have overlooked something fundamental, but by placing a cout statement within the getline based while loop I determined it is only executing once.
I tried multiple changes, such as if (tokens.size()==0), but can't pinpoint the problem. I suppose I'm more comfortable with arrays, but getting it working with either method would be fine.
Ok, further changes yielded better results, but there are still some major issues. By setting up stringstream, it now seems to be grabbing words, yet my output is only the last 3.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
usingnamespace std;
int main()
{
string line;
vector<vector<string> > lines;
ifstream Test ( "Data.txt" );
if (!Test)
{
cout << "There was an error opening the file.\n";
return 1;
}
else
{
//store words from input file in vector
while(getline(Test,line));
{
vector<string> tokens;
stringstream ls(line);
string token;
while (ls>>token)
tokens.push_back(token);
if (tokens.size())
lines.emplace_back(move(tokens));
}
//output vector
for (auto& line : lines)
{
for (auto& token : line)
cout<<token<<" ";
cout<<endl;
}
return 0;
}
}
Calabasas, CA 92136
Needless to say, only the last three words were stored. What can I do to improve on this? Including a cout statement within the inner while loop demonstrated that it is only reading in those three words, and the outer while loop is only executing once. Why is that?
I got it! It was so silly in the end, the semicolon at the end of line 23 was causing all my headaches! And it skipped the blank line just like I wanted it to!
One last question: when populating the vector, how easy would it be to add blank strings ("") for the third position when no third string exists?
Thanks, kevinkjt2000. I know that will work, but how do I get the vector populating loop to recognize the correct circumstances to apply it? I went about it this way, but the downside is in the event any address has more than 3 fields it becomes pointless. How can I apply the same concept regardless of the number of fields involved? Considering that it may encounter addresses that have more fields after processing earlier addresses, is it even possible?
1 2 3 4 5 6 7 8 9 10 11 12
while(getline(Test,line))
{
vector<string> tokens;
stringstream ls(line);
string token;
while (ls>>token)
tokens.push_back(token);
if (tokens.size()<3)
tokens.push_back("");
if (tokens.size())
lines.emplace_back(move(tokens));
}
I'm confused about how that last if statement works, other than the fact that it takes each row and adds it to the vector lines unless it doesn't contain anything, and maybe that's all there is to it, but maybe not. I'm also still wondering how other posters manage to get code or output windows side by side in their posts.
while(getline(Test,line))
{
vector<string> tokens;
stringstream ls(line);
string token;
while (ls>>token)
tokens.push_back(token);
if (tokens.size() == 0)
tokens.resize(1) ; // resize the vector - there will be one default constructed (empty) string in the vector.
lines.emplace_back(move(tokens)) ;
}
I'm also still wondering how other posters manage to get code or output windows side by side in their posts.
Within a code block, place the string "---" (without the quotes) at the beginning of a line. Everything following that will be in a side-by-side output window.
[code]This is the first line in the code window.
This is the second line in the code window.
---
This is in the output window.
[/code]
looks like this:
1 2
This is the first line in the code window.
This is the second line in the code window.