read aligned data in c++ - c++

I want to read data from a file with a quite strange structure. The file looks like this below:
some lines with text....
10 1000 10
1 1 1
1 100 100
.
.
.
some lines with text...
again data like above..
some lines with text... etc
So I have two questions:
How can I read only the specific lines with the data?
How can I read these right aligned data?
Here is one of my trials:
string line;
ifstream myfile ("aa.txt");
double a,b,c;
while (! myfile.eof() )
{
for (int lineno = 0; getline (myfile,line); lineno++)
if (lineno>2 && lineno<5){
myfile>>a>>b>>c;
cout<<lineno<<" " << line << endl;}
}
myfile.close();

how can I read only the specific lines with the data?
well, read all the lines, and then write a function to detect whether the current line is a "data" one or not.
What are the characteristics of your data line? It consists only of digits and spaces? Could there be tabs? What about the columns, are they fixed width? Your predicate function can check there are spaces in the required columns if so.
how can I read these right aligned data?
You want to extract the integer values? Well, you can create a std::istringstream for your line (once you've checked it is data), and then use the >> stream extraction operator to read values into variables of the appropriate type.
Read up on how it handles whitespace (and/or experiment) - it might just do what you need with no effort.

this is just a simple example: you declare 3 variables as you did a, b , c as integer and a string line you open a file and input line convert line to integer if ok assign it to a if not don't do anything to b and c until next read until a valid conversion for a is ok then input b and c and like this:
#include <iostream>
#include <string>
#include <fstream>
int main()
{
std::ifstream in("data.txt");
int a = 0, b = 0, c = 0;
std::string sLine;
if(in.is_open())
{
while(in >> sLine)
{
if( a = atoi(sLine.c_str()))
std::cout << "a: " << a << std::endl;
if(a)
{
in >> sLine;
if( b = atoi(sLine.c_str()))
std::cout << "b: " << b << std::endl;
}
if(b)
{
in >> sLine;
if( c = atoi(sLine.c_str()))
std::cout << "c: " << c << std::endl;
}
}
}
in.close();
std::cout << std::endl;
return 0;
}

Related

Rid of white space in file print

i am trying to get my file to remove the leading and trailing space but it does not work.
this is the txt file contents:
392402 wench
I have tried printing out my code, and this is what is displayed.
first: 392402 wench second:
I want it to display this instead
first: 392402 second: wench
this is my code
void readFile(const string &fileName) {
int limit;
ifstream ifs(fileName);
string::size_type position;
key_type item;
mapped_type count;
string line;
if (ifs.is_open()) {
ifs >> limit;
for (int i = 0; i < limit; i++) {
getline(ifs, line);
position = line.find(" ", 0);
auto c = line.substr(position + 1);
item = line.substr(0, position);
cout << "first: " << c << " second: " << item << endl;
value_type value(item, count);
values.push_back(value);
}
} else {
cout << "Can't open file.";
}
what am i doing wrong? Thank you
The two biggest mistakes you're making are (a) not checking your values for expected output as you go, and (b) not running your code in a debugger to see what is really happening. If you had, the values of position, c, and item would have been blatantly wrong, and you could then surmise where to go from there.
Belaying the highly-likely possibility that the loop iteration is broken from inception because you never consumed the remainder of the entry line containing input, let's look at the actual data and what you're asking of it with your code.
We read this entire line:
392402 wench
You then ask "find the first single-space string in this line" via this code:
position = line.find(" ", 0);
Well, that would be here:
392402 wench
^here
So position is zero (0). You then ask for the sub-string, starting a that position + 1, through the end of the string with this code:
auto c = line.substr(position + 1);
Therefore c now contains (leading space removed via the +1):
392402 wench
Now we build item, which is done with this line:
item = line.substr(0, position);
Remember, position is zero, so you're asking for the string, starting at location 0, length 0. As you can imagine, that isn't going to amount to anything. So now item is an empty string.
Finally, the output statement:
cout << "first: " << c << " second: " << item << endl;
will produce:
first: 392402 wench second:
I.e. exactly what you're seeing. And that's it. Clearly this is wrong.
Alternative
Use better error checking, value checking, and a string stream for per-line extraction. The following code doesn't give two cents about your type aliases (mainly because you didn't include them anyway and I'd rather not loft any guesses as to their origin).
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <limits>
// Expects a file with the following format:
// count
// name1 value1
// name2 value2
// ...
void readFile(const std::string &fileName)
{
std::ifstream ifs(fileName);
if (ifs.is_open())
{
int limit;
if (ifs >> limit && limit > 0)
{
// consume through end of line.
ifs.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
// repeat until `limit` iterations or stream error/eof
std::string line;
for (int i = 0; i < limit && std::getline(ifs, line); i++)
{
std::istringstream iss(line);
// extract line values. Note these *can* be formatted
// extraction for things besides just strings
std::string first, second;
if (iss >> first >> second)
{
std::cout << "first: " << first << " second: " << second << '\n';
// TODO: whatever you want to do with first/second
}
}
}
ifs.close();
}
else
{
std::cerr << "Can't open file: " << fileName << '\n';
}
}
Note: The above code will NOT work for remaining-line-content as the expected second value. E.g. It will not process something like this as you may first expect:
10000 this is a multi-word description
will produce this:
first: 10000 second: this
which is considerably different than what you may be expecting:
first: 10000 second: this is a multi-word description
There was no suggestion in the original post such support was mandatory, though adding it wouldn't be terribly difficult to add. If it is a requirement, I leave that task to you.

How skip reading " : " while taking input from a file in C++

Lets say I want to input the hours, minutes and seconds from the first line of a file and store them to 3 different variables, hrs, mins and sec respectively.
I cant figure out an easy way to skip reading the colon character (":").
Input file example:
12:49:00
Store:
hrs = 12
mins = 59
sec = 00
You can use std::regex to match, range-check and validate your input all at once.
#include <iostream>
#include <regex>
#include <string>
int main()
{
const std::regex time_regex("(\\d|[0,1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d)");
std::smatch time_match;
std::string line;
while (std::getline(std::cin, line))
{
if (std::regex_match(line, time_match, time_regex))
{
int hours = std::stoi(time_match[1]);
int minutes = std::stoi(time_match[2]);
int seconds = std::stoi(time_match[3]);
std::cout << "h=" << hours << " m=" << minutes << " s=" << seconds << std::endl;
}
else
{
std::cout << "Invalid time: " << line << std::endl;
}
}
return 0;
}
See this example live here.
Breaking down the regular expression (\\d|[0,1]\\d|2[0-3]):([0-5]\\d):([0-5]\\d):
\d|[0,1]\d|2[0-3] matches the hour (24-hour time) which is one of:
\d : 0-9
[0,1]\d : 01-19
2[0-3] : 20-23
[0-5]\d matches the minutes: two digits 00-59
[0-5]\d matches the seconds: two digits 00-59, as above.
An alternative not using a temporary character for skipping the colon:
#include <iostream>
int main()
{
int h,m,s;
std::cin >> h;
std::cin.ignore(1) >> m;
std::cin.ignore(1) >> s;
std::cout << h << ':' << m << ':' << s << std::endl;
return 0;
}
This seems to work:
int h, m, s;
char c;
cin >> h >> c >> m >> c >> s;
You just skip : symbol this way. I don't know whether it's a good solution.
With cin.ignore:
cin >> h;
cin.ignore(1);
cin >> m;
cin.ignore(1);
cin >> s;
There are already several good answers and one that has already been accepted; however I like to propose my solution not only as a valid answer to your problem but also in regards to a good design practice. IMHO when it involves reading information from a file and storing it's contents to variables or data structures I prefer to do it in a specific way. I like to separate the functionality and responsibility of specific operations into their own functions:
1: I first like to have a function to open a file, read the contents and to store the information into either a string, a stream or some large buffer. Once the appropriate amount of information is read from the file, then the function will close the file handle as we are done with it and then return back the results. There are several ways to do this yet they are all similar.
a: Read a single line from the file and return back a string or a stream.
b: Read in all information form the file line by line and store each line into its own string or stream and return back a vector of those strings or streams.
c: Read in all of the contents of the file into a single string, stream or large buffer and return that back.
2: After I have the contents of that file then I will typically call a function that will parse that data and these functions will vary depending on the type of content that needs to be parsed based on the data structures that will be used. Also, these parsing functions will call a function that will split the string into a vector of strings called tokens. After the split string function is called then the parsing of data will use the string manipulators-converters to convert a string to the required built in types that are needed for the current data structure that is in use and store them into the data structure that is passed in by reference.
3: There are two variations of my splitString function.
a: One takes a single character as a delimiter.
b: The other will take a string as its delimiter.
c: Both functions will return a vector of strings, based on the delimiter used.
Here is an example of my code using this text file for input.
time.txt
4:32:52
main.cpp
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include <exception>
struct Time {
int hours;
int minutes;
int seconds;
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
std::string getLineFromFile( const char* filename ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
std::getline( file, line );
file.close();
return line;
}
void parseLine( const std::string& fileContents, Time& time ) {
std::vector<std::string> output = splitString( fileContents, ':' );
// This is where you would want to do your sanity check to make sure
// that the contents from the file are valid inputs before converting
// them to the appropriate types and storing them into your data structure.
time.hours = std::stoi( output.at( 0 ) );
time.minutes = std::stoi( output.at( 1 ) );
time.seconds = std::stoi( output.at( 2 ) );
}
int main() {
try {
Time t;
std::string line = getLineFromFile( "time.txt" );
parseLine( line, t );
std::cout << "Hours: " << t.hours << '\n'
<< "Minutes: " << t.minutes << '\n'
<< "Seconds: " << t.seconds << "\n\n";
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Output:
Hours: 4
Minutes: 32
Seconds: 52
Now as you can see in this particular situation the functions that are being used here is designed only to read a single line from the file and of course the very first line from the file. I have other functions in my library not shown here that will read each line of a file until there are no more lines to read, or read all of the file into a single buffer. I have another version of split string that will take a string as its delimiter instead of a single character. Finally for the parsing function, each parsing function will end up being unique due to the fact that it will rely on the data structure that you are trying to use.
This allows the code to be readable as each function does what it is supposed to do and nothing more. I prefer this design over the fact of trying to get information from a file and trying to parse it while the file is open. Too many things can go wrong while the file is open and if the data is read wrong or corrupted but to the point where the compiler doesn't complain about it, then your variables or data structures may contain invalid information without you being aware of it. At least in this way you can open the file, get what you need from the file and store it into a string or a vector of strings, close the file when done reading and return back the contents. Then it becomes the parsing function's responsibility to test the data after it has been tokenized. Now, in the current parsing function that I shown above I did not do any sanity check to keep things simple, but that is where you would test your data to see if the information is valid before returning back your populated data structure.
If you are interested in another version of this where there are multiple lines being read in from the file, just comment a request and I will append it to this answer.

Text file has 1 or 2 or 3 strings followed by doubles. How do I read strings?(each line has 1 or 2 or 3 string in the beginning) [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I'm still new to c++(and to stackoverflow).
I'm creating program to analyze some data for me(open file, save data into vectors, then I search vectors for name, and then I work with objects functions). Anyways, previous "version" of the program works fine, but file that I open only contains 1 string per line in the beginning(followed by other data), so it's easy to work with.
What I'm trying to do, is to open up the files that contain 1-3 strings(i.e. line 1 - West Ham United, line 2 - Everton etc.). After the name, other data follows on the same line.
I thought about separating the name and other data into 2 lines, in example Line1 - name, Line2 - other data, and so on. But how do I save the whole "name" line into vector(vector is of the object type). Other option would be to open up the file and saving all of the chars into an array. Afterwards I would output that array in Outfile, deleting the spaces only between letters(i.e. - WestHamUnited), and then I would work with the output file.
Question is, how do I delete the spaces only between letters?
Maybe you guys could recommend some other option that I'm not aware of.
Let's suppose there is text line stored in string object (s can be read from file/stream by getline)
std::string s = "Manchester City 17 26 2.16";
and we need to read all text before the first number to other string (join some words without spaces)
std::string name = ""; // empty string to store name
We do not know how many non-numbers (parts of name) will be until the first number is in the string, but we expect that number is.
If your task is something like that, consider the following example:
std::istringstream ss(s); // creation of stream object to read data from text line
double d = 0; // will be the first number
std::string str; // buffer string for parts of name
bool notNumber;
// loop that works to update name trying to find a number
do{
ss >> d; // try to read number
notNumber = ss.fail(); // save the state (if number was read value is false)
if (notNumber) // is value is true (not a number is the first in the stream)
{
ss.clear(); // reset the state
ss >> str; // read a word
name.append(str); // add this word to the name
}
} while (notNumber && !ss.eof()); // while non-numbers are read and stream is not empty
// after loop we have name and, perhaps, number
std::cout << "Name is: " << name << std::endl;
std::cout << "The first number is: " << d << std::endl;
UPDATE:
Actually we can use notNumber to know that number was really present in the initial string s
if (notNumber)
std::cout << "There is no number after name!" << std::endl;
else
std::cout << "The first number is: " << d << std::endl;
UPDATE 2:
For file test.txt comprising
West Ham United 3.14 22
Everton 17 26 2.16
Manchester City 9.32 17 26
the following program
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
using namespace std;
int main(void)
{
// data structure to store data from file
map<string, vector<double>> dataTable;
// open the file
ifstream inFile("test.txt");
if (!inFile.is_open())
{
cout << "File error!" << endl;
return 1;
}
// reading data from file
while (!inFile.eof() && inFile.good())
{
string s;
string name;
getline(inFile, s);
istringstream ss(s);
double d = 0;
string str;
bool notNumber;
do{
ss >> d;
notNumber = ss.fail();
if (notNumber)
{
ss.clear();
ss >> str;
name.append(str);
}
} while (notNumber && !ss.eof());
if (name.length() == 0)
continue;
// saving numbers to table
do{
dataTable[name].push_back(d);
ss >> d;
} while (!ss.fail());
}
inFile.close();
// print data stored in table
for (map<string, vector<double>>::iterator i = dataTable.begin(); i != dataTable.end(); i++)
{
cout << i->first << " : ";
for (vector<double >::iterator j = i->second.begin(); j != i->second.end(); j++)
{
cout << *j << " ";
}
cout << endl;
}
return 0;
}
provides the following output:
"Question is, how do I delete the spaces only between letters?"
You are aware of std::string, aren't you? Then you should be able to locate a method that deletes a character or character range from the string.
Good. Than the only problem would be how to detect if a character is a space. How hard could it be, I wonder.
I short: RTFM, Luke, use it with full confidence.
If you can control input file format, I would recommend you to introduce some delimiter mark for the first column.
Otherwise, if the number of columns is fixed, you can read line in reverse. E.g. the latest 3 words are numbers and all the rest is team's name.
Note, that it is wrong just read words until the first digit, because there are exist teams with numbers in name (e.g. FC Schalke 04).
Here is an example:
struct FileLine
{
std::string col1;
double col2 = 0., col3 = 0., col4 = 0.;
// `words` is a vector of words from a line in file
bool load(const std::vector<std::string>& words)
{
col1.clear();
col2 = col3 = col4 = 0.;
std::vector<std::string>::size_type n = words.size();
if (n >= 4) {
col4 = std::stod(words.at(n-1));
col3 = std::stod(words.at(n-2));
col2 = std::stod(words.at(n-3));
col1 = std::accumulate(words.begin(), words.end()-3, col1);
}
return n >= 4;
}
};
Another way is to use regular expressions (link). It's very powerful tool for a text parsing.

Skip non-integers from text file C++

I have a program which reads integers from a text file and skips non-integers and strange symbols. Then text file looks like:
# Matrix A // this line should be skipped because it contains # symbol
1 1 2
1 1$ 2.1 // this line should be skipped because it contains 2.1 and $
3 4 5
I have to print out the matrix without strange symbols and non-integers line. That is the output should be:
1 1 2
3 4 5
My code
ifstream matrixAFile("a.txt", ios::in); // open file a.txt
if (!matrixAFile)
{
cerr << "Error: File could not be opened !!!" << endl;
exit(1);
}
int i, j, k;
while (matrixAFile >> i >> j >> k)
{
cout << i << ' ' << j << ' ' << k;
cout << endl;
}
But it fails when it gets the first # symbol. Anyone helps please ?
Your problem is with this code.
int i, j, k;
while (matrixAFile >> i >> j >> k)
The assignment is "Find out if the line contains integers"
But your code is saying "I already know that the line contains integers"
If you are set to three integers per row, I suggest this pattern:
#include <fstream>
#include <sstream>
#include <string>
std::ifstream infile("matrix.txt");
for (std::string line; std::getline(infile, line); )
{
int a, b, c;
if (!(std::istringstream(line) >> a >> b >> c))
{
std::cerr << "Skipping unparsable line '" << line << "'\n";
continue;
}
std::cout << a << ' ' << b << ' ' << c << std::endl;
}
If the number of numbers per line is variable, you could use a skip condition like this:
line.find_first_not_of(" 0123456789") != std::string::npos
Of course, this fails at the # character: The # isn't an integer and, thus, reading it as an integer fails. What you could do is try to read three integers. If this fails and you haven't reached EOF (i.e., matrixAFile.eof() yields false, you can clear() the error flags, and ignore() everything up to a newline. The error recovery would look something like this:
matrixAFile.clear();
matrixAFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
Note, that you need to bail out if you failed because eof() is true.
Since it's an assignment, I'm not giving full answer.
Read the data line by line to a string(call it str),
Split str into substrings,
In each substring, check if it's convertible to integer value.
Another trick is to read a line, then check that every char is between 0-9. It works if you don't need to consider negative numbers.
I think I'd just read a line at a time as a string. I'd copy the string to the output as long as it contains only digits, white-space and (possibly) -.

why does vector.size() read in one line too little?

when running the following code, the amount of lines will read on less then there actually is (if the input file is main itself, or otherwise)
why is this and how can i change that fact (besides for just adding 1)?
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
// open text file for input
string file_name;
cout << "please enter file name: ";
cin >> file_name;
// associate the input file stream with a text file
ifstream infile(file_name.c_str());
// error checking for a valid filename
if ( !infile ) {
cerr << "Unable to open file "
<< file_name << " -- quitting!\n";
return( -1 );
}
else cout << "\n";
// some data structures to perform the function
vector<string> lines_of_text;
string textline;
// read in text file, line by line
while (getline( infile, textline, '\n' )) {
// add the new element to the vector
lines_of_text.push_back( textline );
// print the 'back' vector element - see the STL documentation
cout << "line read: " << lines_of_text.back() << "\n";
}
cout<<lines_of_text.size();
return 0;
}
The code you have is sound. Here's a small test case that might help:
void read_lines(std::istream& input) {
using namespace std;
vector<string> lines;
for (string line; getline(input, line);) {
lines.push_back(line);
cout << "read: " << lines.back() << '\n';
}
cout << "size: " << lines.size() << '\n';
}
int main() {
{
std::istringstream ss ("abc\n\n");
read_lines(ss);
}
std::cout << "---\n";
{
std::istringstream ss ("abc\n123\n");
read_lines(ss);
}
std::cout << "---\n";
{
std::istringstream ss ("abc\n123"); // last line missing newline
read_lines(ss);
}
return 0;
}
Output:
read: abc
read:
size: 2
---
read: abc
read: 123
size: 2
---
read: abc
read: 123
size: 2
I think I have tracked down the source of your problem. In Code::Blocks, a completely empty file will report that there is 1 line in it (the current one) in the gizmo on the status bar at the bottom of the IDE. This means that were you actually to enter a line of text, it would be line 1. In other words, Code::Blocks will normally over-report the number of actual lines in a file. You should never depend on CB, or any other IDE, to find out info on files - that's not what they are for.
Well, if the last line of your file is just '\n', you don't push it into the vector. If you want it to be there, change the loop to:
while (getline( infile, textline, '\n' ).gcount() > 0)
{
if (infile.fail()) break; //An error occurred, break or do something else
// add the new element to the vector
lines_of_text.push_back( textline );
// print the 'back' vector element - see the STL documentation
cout << "line read: " << lines_of_text.back() << "\n";
}
Use the gcount() member to check how many characters were read in the last read - this will return 1 if it only read a delimiter character.
Ok so here is an explanation that you will hopefully understand. Your code should work fine if the file we're talking about doesn't end with newline. But what if it does? Let's say it looks like this:
"line 1"
"line 2"
""
Or as a sequence of characters:
line 1\nline 2\n
This file has THREE lines -- the last one being empty but it's there. After calling getline twice, you've read all the characters from the file. The third call to getline will say oops, end of file, sorry no more characters so you'll see only two lines of text.