I am writing a program that reads in data from a file. The file contains lines of integers, such as
5 6 2 8 6 7
2 5 3
4 0 9 1 3
The first integer of each line corresponds to how many numbers there are in that line. My goal is to read in each line, store the numbers in a vector, and do some operation on them. Here is what I have done:
int main(){
vector<int> vec;
int amount;
int nums;
ifstream file ("file.txt");
while(!(file.eof())){
file >> amount;
cout << amount << endl;
for (int i = 0; i < amount; i++){
file >> nums;
vec.push_back(nums);
}
printArray(vec);
bubbleSort(vec);
vec.clear();
}
return 0;
}
Unfortunately, the last line always gets read twice. I looked online and saw that the eof() function should not be used to maintain loops. What else could I use in this situation?
Thanks.
operator>> sets the stream's eofbit flag if it tries to read past EOF. You can use that condition to break your loops. But you have to actually perform a read operation BEFORE you can evaluate eof(). See Why is iostream::eof inside a loop condition (i.e. `while (!stream.eof())`) considered wrong? for more details on that.
Since you are dealing with line-based text, you can use std::getline() to read each line first, and then you can use std::istringstream to parse each line, eg:
int main()
{
vector<int> vec;
ifstream file ("file.txt");
string line;
while (getline(file, line)) {
istringstream iss(line);
int amount, nums;
iss >> amount;
cout << amount << endl;
for (int i = 0; (i < amount) && (iss >> nums); ++i){
vec.push_back(nums);
}
printArray(vec);
bubbleSort(vec);
vec.clear();
}
return 0;
}
Alternatively, you can simply take advantage of the fact that operator>> skips whitespace, including line breaks, eg:
int main()
{
vector<int> vec;
int amount, nums;
ifstream file ("file.txt");
while (file >> amount) {
cout << amount << endl;
for (int i = 0; (i < amount) && (file >> nums); ++i){
vec.push_back(nums);
}
printArray(vec);
bubbleSort(vec);
vec.clear();
}
return 0;
}
Although, this approach would be a little less resilient to errors in the input data, compared to the std:getline() approach. If the actual amount of numbers in a given line does not match the specified amount at the beginning of the line, this approach will get its reading of the file out of sync. Worse, if a given line contains any non-integer values, this approach will fail to read any subsequent data at all.
In the std:getline() approach, if a given line is malformed, the code will simply move on to the next line and continue on like nothing bad happened.
Related
I am quite new in c++ and programming so sorry in advance in my question repeats. I have a text file of 3 lines:
7
00000000000000000000000*0000
0 0 0 R 0
What I need to do is read 2nd line and write it into an array as char. But I must not include 3rd line because it will go to a completely different matrix. My code so far :
ifstream input;
input.open("input1.txt");
input >> start;
char a=0;
string line;
while (getline(input, line))
{
a=0;
istringstream iss(line);
int length = line.size();
for (int i=0; i<length; i++)
{
iss >> a;
A[i] = a;
cout << A[i] << " " << i << endl;
}
}
input.close();
However, with this code it always starts new array for 3rd line. What am I doing wrong? What is the easiest way to fix it? Thank you.
-----------------------------Update--------------------------------------
I have modified the code but it still does not work properly. I am getting this kind of result : 5)-└ instead of correct one. My current code:
void Read(int &numLines, int &start, vector<char>&A, char B[][5])
{
ifstream input;
input.open("input.txt");
input >> start;
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
string line;
if(getline(input, line))
{
for(char temp: line)
{
A.push_back(temp);
}
}
input.close();
}
A here is a vector I want to write 2nd line to, char by char
Start is just an integer in which I am storing 1st line (7)
Thank you very much for advices
Mixing >> and std::getline is non-trivial. For example, after input >> start; the end of line marker is left in the stream in case it's still needed. In your case it isn't, and it is picked off by the subsequent call to getline, resulting in a read of an empty line.
This is what's complicating your read of line and forcing the while loop and test for empty lines.
Step through your program with your development environment's debugger and you'll see what I'm talking about. Get used to using the debugger. It's possibly the best programming productivity tool you'll ever encounter.
The easiest way to fix it is to place
input.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
after
input >> start;
to eat up the end of the line (and anything else that might be on that line. This needs the addition of #include<limits> to get std::numeric_limits<std::streamsize>::max.
Then you can remove the while loop and replace it with
if (getline(input, line))
No loop, not chance of consuming multiple lines from the file. And the logic for reading and processing the third line can follow.
Side note: instead of that for loop, consider
int i = 0;
while (iss >> a)
{
A[i] = a;
cout << A[i] << " " << i << endl;
i++;
}
This will march through iss until it hits the end of the line. You can also throw iss out entirely and just read the characters directly out of line.
int i = 0;
for(char temp: line)
{
A[i] = temp;
}
And A should probably be a vector if it isn't already to reduce the chances of buffer overruns.
for(char temp: line)
{
A.push_back(temp);
}
I would go with something like this:
std::string start;
std::string Astring;
ifstream input;
input.open("input.txt");
input >> start;
input >> Astring;
// If you really want a char array
char * A = new char[Astring.size()];
for (unsigned int i = 0; i < Astring.size(); i++) {
A[i] = Astring[i];
}
// Don't forget to delete after use
delete[] A;
Moreover, if you just need the char array as an input to something else later, you can call Astring.c_str() instead of that for loop, which returns a C-style char array.
I have to create a vector of vectors from a text file. The values in question are integers.
The values are a fixed 3 columns with varying rows. However, I don't believe this is causing my issues. The main issue I think I'm having is that the values from the text file aren't being put into the vector of vectors. The relevant code is as follows:
ifstream infile("material_properties.txt");
if (!infile)
{
cout << "File material_properties.txt not found." << endl;
return -1;
}
int lines = 0;
string line;
while (getline(infile, line))
{
++lines;
}
vector< vector<int> > properties(lines,vector<int>(3));
while (getline(infile,line)) {
for(int i=0; i < lines; i++){
for (int j=0; j<4; j++){
infile >> properties[i][j];
}
}
}
I'm very new to coding and very confused.
You need to rewind your ifstream, add:
infile.seekg(0);
before your second while (getline(infile,line)) {
This is because when you read a file, an internal pointer to the current file position is incremented. It incremented until end of file in first getline loop, so in second you need to rewind it.
Your second bug is that in;
vector< vector<int> > properties(lines,vector<int>(3));
you create vector of three elements in your vector of vector, but in the read loop you add four elements from your file. You should change it to vector<int>(4).
Third issue, is your way of parsing file. In your second loop you read file line by line, which indicates you want to parse it, but your code is actually wrong:
int i = 0;
while (getline(infile,line)) {
// This actually makes no sense, you have read one line
// which you should parse and put results into properties vector.
//for(int i=0; i < lines; i++){
// for (int j=0; j<4; j++){
// infile >> properties[i][j];
// }
//}
// parsing would look like this (depends on your input file):
std::istringstream in(line);
in >> properties[i][0] >> properties[i][1] >> properties[i][2];
i++;
}
First off
while (getline(infile, line))
{
++lines;
}
Is going to read in the file until it reaches the end of the file. Then when you go to read from the file again you are already at the end so nothing will be read. Instead of read in from the file to find the file size you can just read from the file and input the values into the vector. The vector will grow as you add data to it automatically.
ifstream infile("material_properties.txt");
vector< vector<int> > properties;
vector<int> row(3);
while (infile >> row[0] >> row[1] >> row[2])
{
properties.push_back(row);
}
There are numbers ranging from 1-4 in a file called "data.txt" (ex. 2 1 2 4 1 3...). I need to read these numbers and create strings of 100 numbers each. After which I need to count how many 1s, 2s 3s, and 4s there are in each string. This is what I have so far:
//structure
struct DNAnt
{
int a, c, g, t;
string strand;
};
void data2DNA();
//main
int _tmain(int argc, _TCHAR* argv[])
{
data2DNA();
cin.get();
return 0;
}
//function
void data2DNA()
{
DNAnt DNAstrand[100];
ifstream inFile;
int numbers;
inFile.open("data.txt");
if(inFile.is_open())
{
while(!inFile.eof())
{
inFile >> numbers;
}
}
else
cout << "ERROR!";
inFile.close();
}
All this program does at the moment is read all the data from the file. I don't know how to continue from here.
Very often in C++ you need to use a loop to repeat an action some number of times. In your case I believe you could benefit from a for loop. Here is an example of a loop that will read 100 integers and store each one to a location in the array that you have near the start of your main:
int i;
for(i = 0; inFile && i < 100; ++i) { //i will range from 0 to 99
inFile >> DNAstrand[i];
}
//now use DNAstrand to do stuff
The reason I say inFile && is to make sure that inFile is not at eof.
Start with a very simple program that reads numbers from a file.
Just read them. Do not interpret them because there is no point to trying to do anything with these numbers until you can prove that they are being read correctly. All additional code will do is muddy the waters of ensuring you are reading correctly and hide the location of bugs from you.
vector<int> readFile()
{
vector<int> numbers;
Why vector? Because it is the easiest way to store an unknown number of entries. Don't play around with more complicated structures at this point and don't try to roll your own. If the instructor says, "No vector" oh well. But that's for what you hand in. What you use to prove the algorithm works is another story.
ifstream inFile("data.txt");
This also opens the file so you don't have to do it later
if(inFile.is_open())
{
int number;
while(inFile >> number)
This reads into an int. If it can't read an int for any reason, it stops and leaves the loop. If the file is poorly formatted and contains data that cannot be converted, we're outta here. If it hits the end of the file, we're outta here.
{
numbers.push_pack(number);
Otherwise, we put the number we read into the vector.
}
}
else
{
cout << "ERROR opening file";
)
Don't need to close the file. When we hit the end of the function inFiles destructor will fire and do it for us.
return numbers;
And send the vector back to the caller so they can use it however they see fit. There is a whole tot of interesting stuff that can happen here to keep the vector from being copied as it is returned, so don't worry about it.
}
All together:
void data2DNA()
{
vector<int> numbers;
ifstream inFile("data.txt");
if(inFile.is_open())
{
int number;
while(inFile >> number)
{
numbers.push_pack(number);
}
}
else
{
cout << "ERROR opening file";
)
return numbers;
}
Now that you have the data in, you can get on with the real job of turning it into a string of nucleic acids.
For this part I'm going to recommend an array
const char tags[] = {'-', 'A', 'C', 'G', 'T'};
Indexes 1 though 4 are A, C, G, and T, so tags[2] will provide 'C'. This makes translating from number to tag really, really easy.
Once you have a tag, you can append it to the DNA string.
i have a text file that has the numbers on one line like (no spaces between commas):
-1,5,-3,10,500000,-6000
so far this is the code i have but i am not getting anything back at all in the vector array
fstream fp;
vector<int> numbers;
int number;
fp.open("numbers.txt", ios::in | ios::binary);
if(fp.is_open()){
while(fp >> number){
numbers.push_back(number);
}
}
fp.close();
cout << "Numbers:\n";
for (int i=0; i < numbers.size(); i++) {
cout << numbers[i] << '\n';
}
i feel like im just not reading the file properly or the whole line is just getting put into the number var. Also, the number of numbers in the file is unknown so i would like to stay away from hardcoding it
fstream fp;
vector<int> numbers;
int number;
fp.open("numbers.txt", ios::in | ios::binary);
if(fp.is_open()){
while(fp >> number){
numbers.push_back(number);
fp.get();
}
}
fp.close();
cout << "Numbers:\n";
for (int i=0; i < numbers.size(); i++) {
cout << numbers[i] << '\n';
}
You just forgot about the comma's! use fp.get(); to get rid of them, and then it'll work fine :D
First of all, your input file is not binary - don't use ios::binary.
Second of all, you need to split your input tokens. Your input operation will fail every time you stumble upon a comma. You might need to input a character or a string to deal with those.
Your input fails when it trys to insert a , into an integer. You should use getline to insert the values up until the comma delimiter:
while (std::getline(fp, number, ','))
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Like this
if(fp.is_open()){
while(fp >> number){
numbers.push_back(number);
char dummy_variable_for_the_comma;
fp >> dummy_variable_for_the_comma; // read and discard a comma
}
}
You have to tell the computer to skip the commas, it won't do that for you.
I have a file that has a number in which is the number of names that follow. For example:
4
bob
jim
bar
ted
im trying to write a program to read these names.
void process_file(ifstream& in, ofstream& out)
{
string i,o;
int tmp1,sp;
char tmp2;
prompt_user(i,o);
in.open (i.c_str());
if (in.fail())
{
cout << "Error opening " << i << endl;
exit(1);
}
out.open(o.c_str());
in >> tmp1;
sp=tmp1;
do
{
in.get(tmp2);
} while (tmp2 != '\n');
in.close();
out.close();
cout<< sp;
}
So far I am able to read the first line and assign int to sp
I need sp to be a counter for how many names. How do I get this to read the names.
The only problem I have left is how to get the names while ignoring the first number.
Until then i cannot implement my loop.
while (in >> tmp1)
sp=tmp1;
This successfuly reads the first int from the and then tries to continue. Since the second line is not an int, extraction fails, so it stops looping. So far so good.
However, the stream is now in fail state, and all subsequent extractions will fail unless you clear the error flags.
Say in.clear() right after the first while loop.
I don't really see why you wrote a loop to extract a single integer, though. You could just write
if (!(in >> sp)) { /* error, no int */ }
To read the names, read in strings. A loop is fine this time:
std::vector<std::string> names;
std::string temp;
while (in >> temp) names.push_back(temp);
You'd might want to add a counter somewhere to make sure that the number of names matches the number you've read from the file.
int lines;
string line;
inputfile.open("names.txt");
lines << inputfile;
for(i=0; i< lines; ++i){
if (std::getline(inputfile, line) != 0){
cout << line << std::endl;
}
}
First of all, assuming that the first loop:
while (in >> tmp1)
sp=tmp1;
Is meant to read the number in the beginning, this code should do:
in >> tmp1;
According to manual operator>>:
The istream object (*this).
The extracted value or sequence is not returned, but directly stored
in the variable passed as argument.
So don't use it in condition, rather use:
in >> tmp1;
if( tmp1 < 1){
exit(5);
}
Second, NEVER rely on assumption that the file is correctly formatted:
do {
in.get(tmp2);
cout << tmp2 << endl;
} while ( (tmp2 != '\n') && !in.eof());
Although whole algorithm seems a bit clumsy to me, this should prevent infinite loop.
Here's a simple example of how to read a specified number of words from a text file in the way you want.
#include <string>
#include <iostream>
#include <fstream>
void process_file() {
// Get file name.
std::string fileName;
std::cin >> fileName;
// Open file for read access.
std::ifstream input(fileName);
// Check if file exists.
if (!input) {
return EXIT_FAILURE;
}
// Get number of names.
int count = 0;
input >> count;
// Get names and print to cout.
std::string token;
for (int i = 0; i < count; ++i) {
input >> token;
std::cout << token;
}
}