Understanding reading txt files in c++ - c++

I am trying to understand reading different txt file formats in c++
I am currently trying to read a file formatted like this,
val1 val2 val3
val1 val2 val3
val1 val2 val3
When I read the file in and then cout its contents I only get the first line then a random 0 0 at the end.
I want to save each value into its own variable in a struct.
I am doing this like this,
struct Input{
std::string group;
float total_pay;
unsigned int quantity;
Input(std::string const& groupIn, float const& total_payIn, unsigned int const& quantityIn):
group(groupIn),
total_pay(total_payIn),
quantity(quantityIn)
{}
};
int main(){
std::ifstream infile("input.txt");
std::vector<Input> data;
std::string group;
std::string total_pay;
std::string quantity;
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);
while(infile) {
data.push_back(Input(group,atof(total_pay.c_str()),atoi(quantity.c_str())));
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);
}
//output
for(Input values : data) {
std::cout << values.group << " " << values.total_pay << " " << values.quantity << '\n';
}
return 0;
}
What is the proper way to read this file in the the format I have specified? Do I need to specify to go to the next line after the third value?
Or should this be taking each value and putting them in to the right variable?
std::getline(infile,group);
std::getline(infile,total_pay);
std::getline(infile,quantity);

Your input processing has a number of issues. Your prevalent usage of std::getline in places where it is not needed isn't helping.
In short, per-line validation of input is generally done with a model similar to the following. Note that this requires the class provide a default constructor. We use an input-string-stream to process a single item from each line of input from the input file. If it was certain there was at-most one per line, we could forego the per-line processing, but it is a potential place for errors, so better safe than sorry. The mantra presented here is commonly used for per-line input validation when reading a stream of objects from a formatted input file, one item per line.
The following code defines the structure as you have it with a few extra pieces, including providing both an input and output stream insertion operator. The result makes the code in main() much more manageable.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iterator>
struct Input
{
// friends not needed if the members are public, but provided here
// in case you ever do make them protected or private (which you should)
friend std::istream& operator >>(std::istream& inp, Input& item);
friend std::ostream& operator <<(std::ostream& outp, Input const& item);
std::string group;
float total_pay;
unsigned int quantity;
// default constructor. sets up zero-elements
Input() : total_pay(), quantity()
{
}
Input(std::string groupIn, float total_payIn, unsigned int quantityIn)
: group(std::move(groupIn))
, total_pay(total_payIn)
, quantity(quantityIn)
{
}
// you really should be using these for accessors
std::string const& getGroup() const { return group; }
float getTotalPay() const { return total_pay; }
unsigned int getQuantity() const { return quantity; }
};
// global free function for extracting an Input item from an input stream
std::istream& operator >>(std::istream& inp, Input& item)
{
return (inp >> item.group >> item.total_pay >> item.quantity);
}
// global operator for inserting to a stream
std::ostream& operator <<(std::ostream& outp, Input const& item)
{
outp << item.getGroup() << ' '
<< item.getTotalPay() << ' '
<< item.getQuantity();
return outp;
}
int main()
{
std::ifstream infile("input.txt");
if (!infile)
{
std::cerr << "Failed to open input file" << '\n';
exit(EXIT_FAILURE);
}
// one line per item enforced.
std::vector<Input> data;
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
Input inp;
if (iss >> inp) // calls our extaction operator >>
data.emplace_back(inp);
else
std::cerr << "Invalid input line: " << line << '\n';
}
// dump all of them to stdout. calls our insertion operator <<
std::copy(data.begin(), data.end(),
std::ostream_iterator<Input>(std::cout,"\n"));
return 0;
}
Provided the input is properly formatted, values like this:
group total quantity
group total quantity
will parse successfully. Conversely, if this happens:
group total quantity
group quantity
group total quantity
total quantity
the extractions of the second and fourth items will fail, and appropriate warning will be issued on std::cerr. This is the reason for using the std::istringstream intermediate stream object wrapping extraction of a single line per item.
Best of luck, and I hope it helps you out.

Check this solution
It is without error checks but with conversion to types
#include<iostream>
#include<sstream>
using namespace std;
int main()
{
string line="v1 2.2 3";//lets say you read a line to this var...
string group;
float total_pay;
unsigned int quantity;
//we split the line to the 3 fields
istringstream s(line);
s>>group>>total_pay>>quantity;
//print for test
cout<<group<<endl<<total_pay<<endl<<quantity<<endl;
return 0;
}

Related

Parsing text file lines in C++

I have a txt file with data such as following:
regNumber FName Score1 Score2 Score3
385234 John Snow 90.0 56.0 60.8
38345234 Michael Bolton 30.0 26.5
38500234 Tim Cook 40.0 56.5 20.2
1547234 Admin__One 10.0
...
The data is separated only by whitespace.
Now, my issue is that as some of the data is missing, I cannot simply do as following:
ifstream file;
file.open("file.txt")
file >> regNo >> fName >> lName >> score1 >> score2 >> score3
(I'm not sure if code above is right, but trying to explain the idea)
What I want to do is roughly this:
cout << "Reg Number: ";
cin >> regNo;
cout << "Name: ";
cin >> name;
if(regNo == regNumber && name == fname) {
cout << "Access granted" << endl;
}
This is what I've tried/where I'm at:
ifstream file;
file.open("users.txt");
string line;
while(getline(file, line)) {
stringstream ss(line);
string word;
while(ss >> word) {
cout << word << "\t";
}
cout << " " << endl;
}
I can output the file entirely, my issue is when it comes to picking the parts, e.g. only getting the regNumber or the name.
I would read the whole line in at once and then just substring it (since you suggest that these are fixed width fields)
Handling the spaces between the words of the names are tricky, but its apparent from your file that each column starts at a fixed offset. You can use this to extract the information you want. For example, in order to read the names, you can read the line starting at the offset that FName starts, and ending at the offset that Score1 starts. Then you can remove trailing white spaces from the string like this:
string A = "Tim Cook ";
auto index = A.find_last_not_of(' ');
A.erase(index + 1);
Alright, I can’t sleep and so decided to go bonkers and demonstrate just how tricky input is, especially when you have freeform data. The following code contains plenty of commentary on reading freeform data that may be missing.
#include <ciso646>
#include <deque>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#include <vector>
// Useful Stuff
template <typename T> T& lvalue( T&& arg ) { return arg; }
using strings = std::deque <std::string> ;
auto split( const std::string& s )
{
return strings
(
std::istream_iterator <std::string> ( lvalue( std::istringstream{ s } ) ),
std::istream_iterator <std::string> ()
);
}
template <typename T>
auto string_to( const std::string & s )
{
T value;
std::istringstream ss( s );
return ((ss >> value) and (ss >> std::ws).eof())
? value
: std::optional<T> { };
}
std::string trim( const std::string& s )
{
auto R = s.find_last_not_of ( " \f\n\r\t\v" ) + 1;
auto L = s.find_first_not_of( " \f\n\r\t\v" );
return s.substr( L, R-L );
}
// Each record is stored as a “User”.
// “Users” is a complete dataset of records.
struct User
{
int regNumber;
std::vector <std::string> names;
std::vector <double> scores;
};
using Users = std::vector <User> ;
// This is stuff you would put in the .cpp file, not an .hpp file.
// But since this is a single-file example, it goes here.
namespace detail::Users
{
static const char * FILE_HEADER = "regNumber FName Score1 Score2 Score3\n";
static const int REGNUMBER_WIDTH = 11;
static const int NAMES_TOTAL_WIDTH = 18;
static const int SCORE_WIDTH = 9;
static const int SCORE_PRECISION = 1;
}
// Input is always the hardest part, and provides a WHOLE lot of caveats to deal with.
// Let us take them one at a time.
//
// Each user is a record composed of ONE OR MORE elements on a line of text.
// The elements are:
// (regNumber)? (name)* (score)*
//
// The way we handle this is:
// (1) Read the entire line
// (2) Split the line into substrings
// (3) If the first element is a regNumber, grab it
// (4) Grab any trailing floating point values as scores
// (5) Anything remaining must be names
//
// There are comments in the code below which enable you to produce a hard failure
// if any record is incorrect, however you define that. A “hard fail” sets the fail
// state on the input stream, which will stop all further input on the stream until
// the caller uses the .clear() method on the stream.
//
// The default action is to stop reading records if a failure occurs. This way the
// CALLER can decide whether to clear the error and try to read more records.
//
// Finally, we use decltype liberally to make it easier to modify the User struct
// without having to watch out for type problems with the stream extraction operator.
// Input a single record
std::istream& operator >> ( std::istream& ins, User& user )
{
// // Hard fail helper (named lambda)
// auto failure = [&ins]() -> std::istream&
// {
// ins.setstate( std::ios::failbit );
// return ins;
// };
// You should generally clear your target object when writing stream extraction operators
user = User{};
// Get a single record (line) from file
std::string s;
if (!getline( ins, s )) return ins;
// Split the record into fields
auto fields = split( s );
// Skip (blank lines) and (file headers)
static const strings header = split( detail::Users::FILE_HEADER );
if (fields.empty() or fields == header) return operator >> ( ins, user );
// The optional regNumber must appear first
auto reg_number = string_to <decltype(user.regNumber)> ( fields.front() );
if (reg_number)
{
user.regNumber = *reg_number;
fields.pop_front();
}
// Optional scores must appear last
while (!fields.empty())
{
auto score = string_to <std::remove_reference <decltype(user.scores.front())> ::type> ( fields.back() );
if (!score) break;
user.scores.insert( user.scores.begin(), *score );
fields.pop_back();
}
// if (user.scores.size() > 3) return failure(); // is there a maximum number of scores?
// Any remaining fields are names.
// if (fields.empty()) return failure(); // at least one name required?
// if (fields.size() > 2) return failure(); // maximum of two names?
for (const auto& name : fields)
{
// (You could also check that each name matches a valid regex pattern, etc)
user.names.push_back( name );
}
// If we got this far, all is good. Return the input stream.
return ins;
}
// Input a complete User dataset
std::istream& operator >> ( std::istream& ins, Users& users )
{
// This time, do NOT clear the target object! This permits the caller to read
// multiple files and combine them! The caller is also now responsible to
// provide a new/empty/clear target Users object to avoid combining datasets.
// Read all records
User user;
while (ins >> user) users.push_back( user );
// Return the input stream
return ins;
}
// Output, by comparison, is fabulously easy.
//
// I won’t bother to explain any of this, except to recall that
// the User is stored as a line-object record -- that is, it must
// be terminated by a newline. Hence we output the newline in the
// single User stream insertion operator (output operator) instead
// of the Users output operator.
// Output a single User record
std::ostream& operator << ( std::ostream& outs, const User& user )
{
std::ostringstream userstring;
userstring << std::setw( detail::Users::REGNUMBER_WIDTH ) << std::left << user.regNumber;
std::ostringstream names;
for (const auto& name : user.names) names << name << " ";
userstring << std::setw( detail::Users::NAMES_TOTAL_WIDTH ) << std::left << names.str();
for (auto score : user.scores)
userstring
<< std::left << std::setw( detail::Users::SCORE_WIDTH )
<< std::fixed << std::setprecision( detail::Users::SCORE_PRECISION )
<< score;
return outs << trim( userstring.str() ) << "\n"; // <-- output of newline
}
// Output a complete User dataset
std::ostream& operator << ( std::ostream& outs, const Users& users )
{
outs << detail::Users::FILE_HEADER;
for (const auto& user : users) outs << user;
return outs;
}
int main()
{
// Example input. Notice that any field may be absent.
std::istringstream input(
"regNumber FName Score1 Score2 Score3 \n"
"385234 John Snow 90.0 56.0 60.8 \n"
"38345234 Michael Bolton 30.0 26.5 \n"
"38500234 Tim Cook 40.0 56.5 20.2 \n"
"1547234 Admin__One 10.0 \n"
" \n" // blank line --> skipped
" Jon Bon Jovi \n"
"11111 22.2 \n"
" 33.3 \n"
"4444 \n"
"55 Justin Johnson \n"
);
Users users;
input >> users;
std::cout << users;
}
To compile with MSVC:
cl /EHsc /W4 /Ox /std:c++17 a.cpp
To compile with Clang:
clang++ -Wall -Wextra -pedantic-errors -O3 -std=c++17 a.cpp
To compile with MinGW/GCC/etc use the same as Clang, substituting g++ for clang++, naturally.
As a final note, if you can make your data file much more strict life will be significantly easier. For example, if you can say that you are always going to used fixed-width fields you can use Shahriar’s answer, for example, or pm100’s answer, which I upvoted.
I would define a Person class.
This knows how to read and write a Person on one line.
class Person
{
int regNumber;
std::string FName;
std::array<float,3> scope;
friend std::ostream& operator<<(std::ostream& s, Person const& p)
{
return p << regNumber << " " << FName << " " << scope[0] << " " << scope[1] << " " << scope[2] << "\n";
}
friend std::istream& operator>>(std::istream& s, Person& p)
{
std::string line;
std::getline(s, line);
bool valid = true;
Person tmp; // Holds value while we check
// Handle Line.
// Handle missing data.
// And update tmp to the correct state.
if (valid) {
// The conversion worked.
// So update the object we are reading into.
swap(p, tmp);
}
else {
// The conversion failed.
// Set the stream to bad so we stop reading.
s.setstate(std::ios::bad);
}
return s;
}
void swap(Person& other) noexcept
{
using std::swap;
swap(regNumber, other.regNumber);
swap(FName, other.FName);
swap(scope, other.scope);
}
};
Then your main becomes much simpler.
int main()
{
std::ifstream file("Data");
Person person;
while (file >> person)
{
std::cout << person;
}
}
It also becomes easier to handle your second part.
You load each person then ask the Person object to validate that credentials.
class Person
{
// STUFF From before:
public:
bool validateUser(int id, std::string const& name) const
{
return id == regNumber && name == FName;
}
};
int main()
{
int reg = getUserReg();
std::string name = getUserName();
std::ifstream file("Data");
Person person;
while (file >> person)
{
if (person.validateUser(reg, name))
{
std::cout << "Access Granted\n";
}
}
}

How to read data in file that contain diffrent size in c++

file_name = file_name_out_of_class;
ifstream file(file_name);
if (file.is_open()) {
string line;
int temp_sap;
int temp_sem;
int temp_cours;
int temp_cred;
while (getline(file, line)) {
cout << sizeof(info) << endl;
//????????
}
}
C++
This pic contains data of the student I want to store in variables or array, but the problem is who to store it. Because 1st line contain large data and 2nd line contain small data.
Since the most important information, I mean, the column header, or field names, or student attribute is missing, I can only give a general answer.
You should abstract the data in one line into a corresponding data structure. I give you an artificial example. A line, as you did show above, could be structured like this:
First an ID, then an item, and then, 0, 1 or more pairs of Evaluators consisting of Descriptors and Numbers. All names are arbitrary chosen. If you give the descripition of the content of one line, then I could use that instead. Anyway.
Then we make an abstraction and create a datatype, a struct/class that can hold the data above.
So, first we have something like:
struct Evaluator {
std::string descriptor{};
unsigned int number{};
};
Next we make a abstraction of complete line data, maybe like that
struct Record {
unsigned int ID{};
unsigned int item{};
std::vector<Evaluator> eval{};
};
the std::vector makes the data dynamic. There can be 0,1 or more Evaluators in there.
And then we will use the C++ method of data inpit and output. The iostream facilities. You know the inserter << operator and the extractor >> operator.
This we will define for our structs.
Then the whole program will look like :
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
struct Evaluator {
std::string descriptor{};
unsigned int number{};
// Extractor
friend std::istream& operator >> (std::istream& is, Evaluator& ev) {
return is >> ev.descriptor >> ev.number;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const Evaluator& ev) {
return os << '\t' << ev.descriptor << '\t' << ev.number;
}
};
struct Record {
unsigned int ID{};
unsigned int item{};
std::vector<Evaluator> eval{};
// Extractor
friend std::istream& operator >> (std::istream& is, Record& r) {
std::string line{};
if (std::getline(is, line)) {
std::istringstream iss(line);
// Delete old data
r.eval.clear();
// Read attributes
iss >> r.ID >> r.item;
// Read all Evaluators
Evaluator temp{};
while (iss >> temp) {
r.eval.push_back(std::move(temp));
}
}
return is;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const Record& r) {
// Write main atributes
os << r.ID << '\t' << r.item;
// Write all evaluators
for (const Evaluator& e : r.eval) os << e;
return os;
}
};
std::istringstream testFile{ R"(7001 2 OOP 4 POM 3 CS 4 Englh 3 Isl.st 2
7002 2 OOP 4 CS 4 Isl.St 2)" };
int main() {
// All records (all lines)
std::vector<Record> record;
// Read all data
Record r;
while (testFile >> r)
record.push_back(std::move(r));
// show debug output
for (const Record& rr : record) std::cout << rr << '\n';
return 0;
}

How to assign class values to vector<string>?

I have a large textfile in which each line represents a city with postalcode and state.
It is written like this:
Brandenburg 35432 Potsdamm
Niedersachsen 35698 Hannover
I've already read the file in a vector and I have written a class and want to assign the classvalues to the vector.
class City
{
private:
float lat; //latitude
float lon; //longitude
public:
City cityclass(std::string state, std::string zipCode, std::string name);
//std::string name;
//std::string state;
//std::string zipCode;
float getLatitude() const
{
return lat;
}
float getLongitude() const
{
return lon;
}
};
So I have a class with std::string zipCode, the state and the cityname. I assume that this would be better to work with, especially when I want to be able to search for city's by zip or name.
How can I realize this? I thought about to simply modify my while-loop as it follows, but I'm realy not sure if this is the way to go.
Here is my full code:
class City
{
private:
/*float lat; //latitude
float lon; //longitude*/
public:
std::string zipCode;
std::string name;
std::string state;
/*float getLatitude() const
{
return lat;
}
float getLongitude() const
{
return lon;
}*/
};
int main ()
{
std::ifstream input("bundesland_plz_ort_de.txt");
//initilazing a vector of type string to store the data
std::vector<City> cityVector;
City city; //creating instance of class
//check if file can be accessed
if(!input)
{
std::cout << "ERROR!\tFile could not be opened!" << std::endl;
}
else
{
while(input >> city.state >> city.zipCode >> city.name)
{
cityVector.push_back(city);
}
input.close(); // close after finishing
}
}
I would recommend a slightly different approach. You defined a class City, but you read the data outside of the class.
In C++ we should put data on operations that are working on that data, all in one class. In this case you would (in your class) overwrite the inserter and extractor operator. The class knows, how to read and write its data. Even if you change the algorithm later, the rest of the code will work without modification.
In the following example code I put an ultra simple extractor and inserter (No error checking). Reading all data from the source file is then just one simple statement.
It is the definition of the variable "cl" as std::vector, using its range constructor. Very short and simple.
Usage of range constructor for std::vector.
We can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
Having modified the class as described, you can then use all algorithm from the std library.
Please see:
#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
struct City {
// Data
std::string state{};
std::string zipCode{};
std::string name{};
// Member functions
// Extractor
friend std::istream& operator >> (std::istream& is, City& c) {
return is >> c.state >> c.zipCode >> c.name;
}
// Inserter
friend std::ostream& operator << (std::ostream& os, const City& c) {
return os << "\nState: " << c.state << "\nZip Code: " << c.zipCode << "\nName: " << c.name;
}
};
int main() {
// Try to open file and check, if it worked
if (std::ifstream sourceFile("r:\\bundesland_plz_ort_de.txt"); sourceFile) {
// Read complete source file into a city list
std::vector cl(std::istream_iterator<City>(sourceFile), {});
// Give some Debug output
std::copy(cl.begin(), cl.end(), std::ostream_iterator<City>(std::cout, "\n"));
}
else {
std::cerr << "\nError: Source file could not be opened\n";
}
}
Of course there are many other possible solutions . . .
just go on
first obtaining the number of object approximately by a loop precede it to count the total space delimited string then it's divided by the number of member data
// ...
//City city; //creating instance of class
if(!input)
{
std::cout << "ERROR!\tFile could not be opened!" << std::endl;
}
else if (input.is_open()) {
string buf(25);
int i=0;
while( input >> buf) ++i;
std::vector<City> cityVector(i /3 +5); // the number of member data plus some extra for sureness
input.clear();
input.seekg(0, ios::beg); // point back to start
i=0;
while( input >> cityVector[i].state >> cityVector[i].zipCode >> cityVector[i].name) ++i;
input.close(); // close after finishing
}
//..
this way need estimate the array size earlier approximately plus some guarding amount, then can resize, refit it eg. cityVector.resize(i+1)

C++ Extraction Operator for Class Functions

I'm not sure if operator overloading is what I'm looking for, but I need to know the best way to achieve the following in C++;
I have a class Employee (for simplicity) with just an ID number atm. Please assume the input file has an int number and some characters after (1 line shown only), such as:
1234 Charles Hammond
Here is the code so far. I am trying to use the extraction operator to get the integer and other data from input file to my class function (SetID);
class Employee
{
int employeeID;
public:
void SetID(int);
}
void Employee::SetID(int empID)
{
employeeID = empID;
}
int main(void)
{
int lineCounter = 4;
Employee emp;
//Create filestream objects and open
ifstream input;
ofstream output;
input.open("input.txt");
output.open("output.txt");
for (int i = 0; i < lineCounter; i++)
{
input >> emp.SetID(input.get()); //illegal? Best way to do this
}
//Close program
output.close();
input.close();
system("pause");
return 0;
}
I am simply trying to get the ID from the input file and store it in the class member "employeeID" to be used for calculations later.
One option is to overload the >> operator and make it a friend function in your Employee class.
Something like:
istream& operator>>( istream& in, Employee& emp )
{
in >> emp.employeeID;
return in;
}
And in your Employee class:
friend istream& operator>> (istream& in, Employee& emp);
There are a ton of ways to do this, each with pluses and minuses. The format of the data you're reading indicates you have one "record" per line, in which case that should be enforced somehow. The following does that by reading a line of data from the input file, then sending that line through a string stream for further processing:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
class Employee
{
// friend this operator, as we'll give it access to our
// private data members of our class.
friend std::istream& operator >>(std::istream& inp, Employee& obj);
int employeeID;
public:
void setID(int id) { employeeID = id; }
int getID() const { return employeeID; }
};
// extracts a single employee from a single input line taken from
// the passed input stream. the code below mandates one, and only
// one employee per line.
std::istream& operator >>(std::istream& inp, Employee& obj)
{
// used as a full-line-buffer to enforce single-record-per-line
std::string line;
if (std::getline(inp, line))
{
// think of it as an in-memory stream primed with our line
// (because that's exactly what it is).
std::istringstream iss(line);
// TODO: eventually you'll want this to parse *all* values from
// the input line, not just the id, storing each in a separate
// member of the Employee object being loaded. for now we get
// only the id and discard the rest of the line.
if (!(iss >> obj.employeeID))
{
// a failure to read from the line string stream should flag
// the input stream we read the line from as failed. we also
// output the invalid line to std::cerr.
inp.setstate(std::ios::failbit);
std::cerr << "Invalid format: " << line << std::endl;
}
}
return inp;
}
int main()
{
// open input and output files
std::ifstream input("input.txt");
// read at-most four employee lines from our file. If there are
// less than that or a read-error is encountered, we will break
// early.
Employee emp;
for (int i=0; i<4 && input >> emp; ++i)
{
// do something with this thing
std::cout << "Read Employee: " << emp.getID() << '\n';
}
system("pause");
return 0;
}

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

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
Try:
Note: if chr can contain more than 1 character then use a string to represent it.
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;
}
Unless you intend to use this struct for C as well, I would replace the intended char* with std::string.
Next, as I intend to be able to read it from a stream I would write the following function:
std::istream & operator>>( std::istream & is, myStruct & my )
{
if( std::getline(is, my.str, '\t') )
return is >> my.v1 >> my.v2;
}
with str as the std::string member. This writes into your struct, using tab as the first delimiter and then any white-space delimiter will do before the next two integers. (You can force it to use tab).
To read line by line you can either continue reading these, or read the line first into a string then put the string into an istringstream and call the above.
You will need to decide how to handle failed reads. Any failed read above would leave the stream in a failed state.
std::ifstream in("fname");
while(in){
std::string line;
std::getline(in,line);
size_t lasttab=line.find_last_of('\t');
size_t firsttab=line.find_last_of('\t',lasttab-1);
mystruct data;
data.chr=line.substr(0,firsttab).c_str();
data.v1=atoi(line.substr(firsttab,lasttab).c_str());
data.v2=atoi(line.substr(lasttab).c_str());
}
I had some difficulty following some of the suggestions here, so I'm posting a complete example of overloading both input and output operators for a struct over a tab-delimited file. As a bonus, it also takes the input either from stdin or from a file supplied via the command arguments.
I believe this is about as simple as it gets while adhering to the semantics of the operators.
pairwise.h
#ifndef PAIRWISE_VALUE
#define PAIRWISE_VALUE
#include <string>
#include <iostream>
struct PairwiseValue
{
std::string labelA;
std::string labelB;
float value;
};
std::ostream& operator<<(std::ostream& os, const PairwiseValue& p);
std::istream& operator>>(std::istream& is, PairwiseValue& p);
#endif
pairwise.cc
#include "pairwise.h"
std::ostream& operator<<(std::ostream& os, const PairwiseValue& p)
{
os << p.labelA << '\t' << p.labelB << '\t' << p.value << std::endl;
return os;
}
std::istream& operator>>(std::istream& is, PairwiseValue& p)
{
PairwiseValue pv;
if ((is >> pv.labelA >> pv.labelB >> pv.value))
{
p = pv;
}
return is;
}
test.cc
#include <fstream>
#include "pairwise.h"
int main(const int argc, const char* argv[])
{
std::ios_base::sync_with_stdio(false); // disable synch with stdio (enables input buffering)
std::string ifilename;
if (argc == 2)
{
ifilename = argv[1];
}
const bool use_stdin = ifilename.empty();
std::ifstream ifs;
if (!use_stdin)
{
ifs.open(ifilename);
if (!ifs)
{
std::cerr << "Error opening input file: " << ifilename << std::endl;
return 1;
}
}
std::istream& is = ifs.is_open() ? static_cast<std::istream&>(ifs) : std::cin;
PairwiseValue pv;
while (is >> pv)
{
std::cout << pv;
}
return 0;
}
Compiling
g++ -c pairwise.cc test.cc
g++ -o test pairwise.o test.o
Usage
./test myvector.tsv
cat myvector.tsv | ./test