I have some trouble with reading of a file in C++. I am able to read only integers or only alphabets. But I am not able to read both for example, 10af, ff5a. My procedure is as follows:
int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << "You should provide a file name." << std::endl;
return -1;
}
std::ifstream input_file(argv[1]);
if (!input_file) {
std::cerr << "I can't read " << argv[1] << "." << std::endl;
return -1;
}
std::string line;
for (int line_no = 1; std::getline(input_file, line); ++line_no) {
//std::cout << line << std::endl;
-----------
}
return 0;
}
So what I am trying to do is, I am allowing the user to specify the input file he wants to read, and I am using getline to obtain each line. I can use the method of tokens to read only integers or only alphabets. But I am not able to read a mix of both. If my input file is
2 1 89ab
8 2 16ff
What is the best way to read this file?
Thanks a lot in advance for your help!
I'd use a std::stringstream, and use std::hex since 89ab and 16ff look like hex numbers.
Should look like this:
std::string line;
for (int line_no = 1; std::getline(input_file, line); ++line_no)
{
std::stringstream ss(line);
int a, b, c;
ss >> a;
ss >> b;
ss >> std::hex >> c;
}
You will need to #include <sstream>
Using
std::string s;
while (input_file >> s) {
//add s to an array or process s
...
}
you can read inputs of type std::string which could be any combination of digits and alphabets. You don't necessarily need to read input line by line and then try to parse it. >> operator considers both space and newline as delimiters.
Related
I am trying to figure out how to extract values from a text file as a type double so that they can be used in calculations.
I have a text file that is formatted like:
parameter1 parameter2 parameter3
50 0 0.1
And I want to extract only the numbers.
This is one of my attempts (I have been working for hours trying to figure out how to do this).
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
const int MAX = 80;
char buffer[MAX];
string input;
double j;
ifstream param0;
param0.open("param0.txt");
char ch;
while (param0)
{
param0.get(ch);
cout << ch;
}
getline(param0, input);
param0 >> j;
while (param0)
{
cout << j << endl;
getline(param0, input);
param0 >> j;
}
return 0;
}
this code
char ch;
while (param0)
{
param0.get(ch);
cout << ch;
}
runs to the end of the file. All reads after it will return nothing. Either take this loop out or rewind the file param0.rewind()
The basic idea of the code should be:
std::string line;
double p1, p2, p3;
std::string more;
while (std::getline(in, line)) {
std::istringstream iss{line};
if (iss >> p1 >> p2 >> p3 && !(iss >> more)) {
std::printf("p1=%f, p2=%f, p3=%f\n", p1, p2, p3);
} else {
std::printf("invalid line: %s\n", line.c_str());
}
}
In plain words, the code says: a line is valid if it contains three numbers and nothing more.
C++ purists will say that I shouldn't use printf in C++, but I like the separation between formatting and the actual data.
suggestion :
best to check if you open the file correctly.
close the file once you finish with it.
you can just use the >> operator if you reading everything in one line. It doesn't matter if its string or double as long as if you pass the correct storage variable;
string param1;
string param2;
string param3;
double j,k,l;
ifstream file("test.txt",std::ios::in);
if (!file.is_open())
std::cout << "failed to open " << endl;
while (file)
{
file >> param1 >> param2 >>param3; // getting as strings
cout << param1 <<", "<< param2<<", "<< param3<<endl;
file >> j >> k >> l; //getting as doubles
cout << j <<", " << k <<", " << l<<endl;
}
file.close();
return 0;
output
parameter1, parameter2, parameter3
50, 0, 0.1
Let's say I have a text file:
83 71 69 97Joines, William B.
100 85 88 85Henry, Jackson Q.
And I want to store each number in an array of ints, and each full-name into an array of strings (a full name would be Joines, William B for example).
What would be the best way, because I debated whether using while (inputFile >> line) or while (getline(inputFile, line)) would be better. I don't know if it would be easier to read them one word at a time or read them one line at a time. My main problem will be splitting the 97Joines, William B. to 97 and Joines, William B. which I don't understand how to do in C++.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
int counter = 0;
int scores[40];
string names[10];
string filename, line;
ifstream inputFile;
cout << "Please enter the location of the file:\n";
cin >> filename;
inputFile.open(filename);
while (inputFile >> line) {
// if line is numeric values only, do scores[counter] = line;
// if it is alphabet characters only, do names[counter] = line;
//if it is both, find a way to split it // <----- need help figuring out how to do this!
}
inputFile.close();
}
You need to #include <cstdlib> for strtol I am sure there are better ways to do this but this is the only way I know and this is only for 97joines, and 85Henry,
string word; // to get joines,
string str; // for 97
string numword;
inputFile >> numword;
for(int k = 0; k < numword.length(); k++)
{
if(isdigit(numword[k]))
{
str = str + numword[k];
}
else
{
word = word + numword[k];
}
}
int num = strtol(str.c_str(), NULL, 0);
You can, given the file structure you have shown, read it like this:
int a, b, c, d;
std::string name;
for (int i = 0; i < 2; ++i)
{
// read the numbers
inputFile >> a >> b >> c >> d;
// read the name
std::getline(inputFile, name);
// do stuff with the data... we just print it now
std::cout << a << " " << b << " " << c << " " << d << " " << name << std::endl;
}
Since the numbers are space separated it is easy to just use the stream operator. Furthermore, since the name is the last part we can just use std::getline which will read the rest of the line and store it in the variable name.
You can try it here, using std::cin.
I have a code like this, concerning stringstream. I found a strange behavior:
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main()
{
int p, q;
fstream file;
string str;
stringstream sstr;
file.open("file.txt", ios::in);
if(file.is_open()) {
while(getline(file, str)) {
sstr << str;
sstr >> p >> q;
cout << p << ' ' << q << endl;
sstr.str("");
}
}
file.close();
return 0;
}
Suppose I have file.txt as
4 5
0 2
with return after 5 in the first line and 2 in the second line. The program gives me:
4 5
4 5
which means p and q are not correctly assigned. But I checked that each time sstr.str() with get the correct string of the line.
Why stringstream has a behaviour like this?
The stream is in a non-good state after reading the second integer, so you have to reset its error state before resuming.
Your real mistake was to not check the return value of the input operations, or you would have caught this immediately!
The simpler solution may be to not try to reuse the same stream, but instead make it anew each round:
for (std::string line; std::getline(file, line); )
{
std::istringstream iss(line);
if (!(iss >> p >> q >> std::ws) || !iss.eof())
{
// parse error!
continue;
}
std::cout << "Input: [" << p << ", " << q << "]\n";
}
When you read p, then q, you reach the end of your stream and the flag eofbit is set and you can't do anything anymore.
Just clear() it and your code will work as you expect.
But you may want to use directly file instead, and file.close(); will have a better place within your if:
fstream file;
file.open("file.txt", ios::in);
if(file.is_open()) {
int p, q;
while(file >> p >> q) {
cout << p << ' ' << q << endl;
}
file.close();
}
Your code has some redundant lines: fstream could be opened during the definition and no explicit file close() is needed, as it is automatically destroyed at the end of main().
Additionally, in your file reading loop, the line: sstr << str should be replaced with stringstream sstr(line); if you want to initialize a new stringstream for each line, which will make the line: sstr.str(""); redundant as well.
Applying the above corrections, here is your code:
int main() {
int p, q;
fstream file("file.txt", ios::in);
// check status
if (!file) cerr << "Can't open input file!\n";
string line;
// read all the lines in the file
while(getline(file, line)) {
// initialize the stringstream with line
stringstream sstr(line);
// extract line contents (see Note)
while (sstr >> p >> q) {
// print extracted integers to standard output
cout <<"p: " << p <<" q: "<< q << endl;
}
}
return 0;
}
Note: The line while (sstr >> p >> q) assumes that a line contains only integers, separated by white space.
Okay, so I have an input file input.txtthat contains a CSV sequence: 1,1,1,2,2,3,3,4,4
and I am trying to separate it at the commas using a stringstream; however I'm getting a little problem here. For some reason the first number from the sequence is not even getting read by the stream. To show this, I created a some debugging code to see what is happening and I found out that the first number is being stored inside csvLine and every other number is being read and coverted just fine. I don't understand why just the first number is being omitted. Below is an example pic showing exactly what I mean. num should have the same exact values and Line, but it's not. It has all the values except the first one, which is being stored inside csvLine. Why is this happening?!
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main(int argc, const char * argv[]) {
ifstream file;
string line;
string csvLine; //comma seperated value line
int num = 0;
file.open(argv[1]);
if(file.is_open()) {
while(getline(file, line)) { //get the whole line and use string stream to break at commas
cout << "\nLine: " << line << endl;
//using stringstream to seperate at commas
stringstream ss(line);
while(getline(ss, csvLine, ',')) {
cout << "csvLine: " << csvLine << " " << endl;
//using stringstream to convert to int
ss >> num;
cout << "num: " << num << " " << endl;
}
}
}
return 0;
}
The problem arises since you're using getline and then extracting integer from your stringstream
You should only use getline
while(getline(ss, csvLine, ','))
{
cout << "csvLine: " << csvLine << " " << endl;
num = std::stoi( csvLine ) ;
}
When you read getline(ss, csvLine, ',') the first time it reads the number followed by the the ','. For the next numbers it just reads the comma as the number was already extracted using ss >> num. That is, the simplest fix is to only read everything up to and including the comma after the loop was executed. Since the value of string extracting the comma isn't used it makes sense to use ignore() instead of std::getline():
for (; ss >> num; ss.ignore(std::numeric_limits<std::streamsize>::max(), ',')) {
// do something with the number
}
Restructuring the loop this way has the added benefit that it is checked whether reading the number was successful.
I have a .csv file that has 3 rows and 5 columns with values of 0,1,2,3,50, or 100. I saved it from an excel sheet to a .csv file. I am trying to use C++ to read in a .csv file and output the first two column values in the .csv file into a text file based on the last three column values. I am assuming the .csv file looks like
1,1,value,value,value
1,2,value,value,value
1,3,value,value,value
But I couldn't find a whole lot of documentation on the format of .csv files.
I looked at Reading Values from fields in a .csv file? and used some of the code from there.
Here is my code:
#include <iostream>
#include <fstream>
using namespace std;
char separator;
int test_var;
struct Spaxel {
int array1;
int array2;
int red;
int blue_o2;
int blue_o3;
};
Spaxel whole_list [3];
int main()
{
// Reading in the file
ifstream myfile("sample.csv");
Spaxel data;
int n = 0;
cout << data.array1<< endl;
myfile >> data.array1; // using as a test to see if it is working
cout << data.array1<< endl;
while (myfile >> data.array1)
{
// Storing the 5 variable and getting rid of commas
cout<<"here?"<< endl;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> data.array2;
// Skip the separator
myfile >> separator;
// Read in next value.
myfile >> data.red;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> data.blue_o2;
// Skip the separator
myfile >> separator;
// Read in next value.
myfile >> data.blue_o3;
// Ignore the newline, as it is still in the buffer.
myfile.ignore(10000, '\n');
// Storing values in an array to be printed out later into another file
whole_list[n] = data;
cout << whole_list[n].red << endl;
n++;
}
myfile.close();
// Putting contents of whole_list in an output file
//whole_list[0].red = whole_list[0].array1 = whole_list[0].array2 = 1; this was a test and it didn't work
ofstream output("sample_out.txt");
for (int n=0; n<3; n++) {
if (whole_list[n].red == 1)
output << whole_list[n].array1 <<","<< whole_list[n].array2<< endl;
}
return 0;
}
When I run it in Xcode it prints three 0's (from the cout << data.array1<< endl; and cout << data.array1<< endl; in the beginning of the main() and from the return 0) but does not output any file. Apparently the .csv file isn't getting read in correctly and the output file is not getting written correctly. Any suggestions?
Thanks for your time!
There are a couple of problem areas in the code you presented:
Hard coded filename. Running your program in a directory that doesn't have "sample.csv" could cause the ifstream failure you're seeing.
No checking whether myfile opened successfully or not.
Loop can access an out-of-bound index in whole_list if "sample.csv" has more lines.
The refactored code below, while not completely foolproof, corrects many of the issues mentioned. It should get you most of the way there.
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
struct Spaxel
{
int array1;
int array2;
int red;
int blue_o2;
int blue_o3;
};
ostream& operator << (ostream &os, const Spaxel &rhs)
{
os << rhs.array1
<< ','
<< rhs.array2
<< ','
<< rhs.red
<< ','
<< rhs.blue_o2
<< ','
<< rhs.blue_o3;
return os;
}
istream& operator >> (istream &is, Spaxel &rhs)
{
char delim;
is >> rhs.array1
>> delim
>> rhs.array2
>> delim
>> rhs.red
>> delim
>> rhs.blue_o2
>> delim
>> rhs.blue_o3;
return is;
}
int main(int argc, const char *argv[])
{
if(argc < 2)
{
cout << "Usage: " << argv[0] << " filename\n";
return 1;
}
const char *infilename = argv[argc - 1];
// Reading in the file
ifstream myfile(infilename);
if(!myfile)
{
cerr << "Couldn't open file " << infilename;
return 1;
}
vector<Spaxel> whole_list;
string line;
while( getline(myfile, line) )
{
Spaxel data;
stringstream linestr (line);
linestr >> data;
whole_list.push_back(data);
cout << data << '\n';
}
}
Edit: Just to help clarify some things from the comment.
As you know main is the entry point of your program so it isn't something called by your own code. The extra optional parameters int argc, const char *argv[], is how options and parameters get passed in when you run your program with arguments. First parameter argc indicates how many arguments were passed in. The second argv is an array of char * with each element being the argument passed. The first argument argv[0] is your program name and so argc is always >= 1.
Say you execute your sample program from the shell:
./sample sample.csv
then argc and argv will have the following:
argc = 2;
argv[0] = "sample"
argv[1] = "sample.csv"
So const char *infilename = argv[argc - 1]; gets the last argument passed in which should be the filename to read in.
Sorry i am not doing it within struct but i hope you will got it and resolve your problem.
char separator;
int value1;
int value2;
int value3;
while (myfile >> value1)
{
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value2;
// Skip the separator, e.g. comma (',')
myfile >> separator;
// Read in next value.
myfile >> value3;
// Ignore the newline, as it is still in the buffer.
myfile.ignore(10000, '\n');
}
The above code fragment is not robust but demonstrates the concept of reading from a file, skipping non-numeric separators and processing the end of the line. The code is optimized either.