reading specific integers from text file - c++

I have a text file that contains this information.
GO
pink colour 60 5 0
pink colour 80 10 0
chocs
red colour 100 15 1
red colour 120 15 1
man
blue colour 140 20 2
fast place
Brown colour 160 20 2
Going in plane
Green colour 280 35 5
Im trying to only extract the first integer of every line. the lines that dont have any integers I can skip.
so i can skip line 1 (Go)
but i need 60 from line 2.
and 80 from line 3.
skip line 4. etc...
but i dont know how. any help is much appreciated. thanks.

You could read the file one character at a time and check if that character is a digit. If it is then continue reading until you hit a character that isn't a digit. Then ignore everything until the character is a newline. It could look something like:
char buff;
std::fstream fin("file", std::fstream::in);
buff = fin.getchar();
// Read until end of file
while (buff != file.EOF)
{
// if the char we read is a digit...
if (isdigit(buff))
{
// Continue to read characters if they are an integer (same number)
while (isdigit(buff))
{
// Store buff in some container
}
// Ignore until we hit the end of that line
fin.ignore(256, '\n');
}
}

pseudocode:
read in a line, quit at EOF
for each line
do a **string::find_first_of** for a digit
if digit not found, go to read the next line
do a **std::stoi** to convert to integer
process the integer

You can do like this:
#include <iostream>
#include <sstream>
using namespace std;
... ...
string str;
while (getline(file, str)) // read each line
{
istringstream iss(str);
int value;
string temp;
if (iss >> temp >> temp >> value) // try to read the value
{
// you got the value here
}
}

Related

Is cin.get() reading multiple digit characters at once?

We were asked to write a simple C++ program by our teacher to add two numbers in the following format:
input: 12 14
output: m+n = 26
The program must also work for other inputs in the form:
input: Hello please add 12 and 14 !
output: m+n = 26
The solution that was given was:
#include <iostream>
using namespace std;
int main(){
int m,n;
char ch;
while(cin.get(ch)){
if(isdigit(ch))
{
cin.putback(ch);
cin>>m;
break;
}
}
//cin.putback() restores the last character
//read by cin.get() back to the input stream
while(cin.get(ch)){
if(isdigit(ch))
{
cin.putback(ch);
cin>>n;
break;
}
}
cin.ignore(80,'\n');
cout<<"m + n = "<<m+n<<endl;
return 0;}
But now I need to know why this program also works for numbers that are not single digits. Shouldn't cin.get(char) just read one digit and cin.putback() return the same? Please help me I am a beginner.
Yes, cin.get() will read only one character at a time.
The important part, where the number is actually read, is 4 lines below: cin>>m;. This will consume as many digits as possible and store the resulting integer in m.
Some more details:
// example with input 524 42
while(cin.get(ch)) // extract one character at a time
{ // ch='5', remaining input="24 42"
if(isdigit(ch)) // if it's a digit (0-9), then:
{
cin.putback(ch); // Put it back into the stream, "unread" it
// remaining input="524 42"
cin >> m; // extract an integer from stream
// m=524, remaining input="42"
break;
}
}
The reason for the loops seems to be to skip over any non-numeric input before a number appears. Note that there is a little bug here, since it will also ignore leading signs. (e.g. input -4 4 will output m + n = 8)

std::cin with more complex objects

I have a text file which first describes some lines and then describes some colorful lines:
1 2 3 4
5 6 7 8
9 10 11 12
red 1 0 0 1 2 3 4
green 0 1 0 5 6 7 8
blue 0 0 1 9 10 11 12
The number of lines in each section is unknown at time of execution
I overloaded the std::cin >> operator for these structs:
struct Point { int x, y; }
struct Line { Point a, b; }
struct Color { float r, g, b; std::string name; };
struct ColorfulLine { Line line; Color color; };
(Full example here: http://ideone.com/bMOaL1 [already works - edited according to the accepted answer])
Now I need to iterate over the file using Lines and ColorfulLines:
Line line;
while(cin >> line) { cout << "We've got a line!\n"; }
ColorfulLine color_line;
while(cin >> color_line) { cout << "We've got a colorful line!\n"; }
// actually I'm putting them into std::lists but I skipped this part for simplicity
And here is the problem - the colorful lines are never fetched, i.e. the second loop is not executed.
I have an assumption why it happens but don't know how to fix it:
When std::cin tries to fetch the 4th Line, it fails because instead of a number there's the string "red".
Next, in the second while loop, std::cin tries to read a string (Color.name) but it sees a number, and then fails too.
I tried to put some random word before the ColorfulLines section hoping that when the first loop fails, the second will start reading from the "red" string, but it didn't.
How to fix this?
After the first loop breaks, std::cin is in a bad state. That's why the loop breaks in the first place. Performing reads on a bad stream fails immediately, thus the second loop is never entered.
To solve this, reset the error state after the first loop breaks with
std::cin.clear();

Reading an ASCII file with fstream

Have an ASCII text file with some integer numbers in it, each separated by a space, and sometimes the numbers go on to a new line. For example:
// my_file.txt
23 45 973 49
44 1032 33 99
43 8 4 90824
I want to read 100 of these numbers into an array of "ints". Thus far, I have the following code:
int x[100];
fstream file_in("my_file.txt", ios::in);
for (int i = 0; i < 100; i++)
{
file_in >> x[i];
}
However, I now want to do a couple of other things that I am not sure about.
What if I want to just read the entire file in, without having to go through the loop? If this was binary data, I know that I can just write file_in.read((char*)&x[0], 100 * sizeof(int)). But how can I do this with an ASCII file?
What if I want to skip the first 3 numbers in the file, and start reading from the fourth? Again, if this was binary data, I could just use file_in.seekg(3 * sizeof(char)). But I don't know how to skip in an ASCII file.
No raw loops!
Reading the entire file:
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
std::ifstream f("data.txt");
std::vector<int> v(std::istream_iterator<int>(f), {});
Skipping over the first three:
v.erase(v.begin(), v.begin() + 3);

Get string from input file until integer? C++

One of my input file, which looks like this:
Stroustrup, Bjarne 8 8 -1 -1 -1
Lovelace, Ada 1 60 14 43 -1
von Neumann, Jon 77 48 65 -1 -1
Wirth, Niklaus 51 59 -1 -1 -1
Wozniak, Steve 81 -1 -1 -1 -1
Babbage, Charles 31 92 -1 -1 -1
Hopper, Grace 76 -1 -1 -1 -1
Bird, Tweety -99 -99 -99 -99 -99
Sylvester 77 39 -1 -1 -1
My current program streams the data using
infile >> lastName >> firstName >> ...
Unfortunately this only worked with the other input files because every line actually had a last and first name. Here, because of the two part last name in the third line and only the first name in the last line, the rest of the data fails to stream. Is there any method to grab a string from the beginning of the line until it reaches an integer?
While parsing input files, you'll almost never end up with solution that directly uses original stream to fill your variables with values. Format of input might differ, errors might occur... Better approach in this case would be reading the input line by line and processing each line separately. While processing each line you can construct a temporary istringstream that you can use to read words from it and check whether the word is convert-able to number or not (if 0 is not valid value, use std::atoi):
std::string line;
while (std::getline(infile,line))
{
if (line.empty()) continue;
std::istringstream is(line);
std::string word;
while (is >> word)
{
int val = std::atoi(word);
if (val)
{
// TODO: number
}
else
{
// word
}
}
}
Alternatively you might consider using std::isdigit to just check whether first character of word is digit or not:
if (std::isdigit(word[0])) ...
Do you have any control over the format of your input file? Seeing as you both the option to have a two-part name (e.g., "von Neumann") or a single name (i.e., not first and last, e.g., "Sylvester"), it's going to be unnecessarily difficult to parse. If you could quote name, like "von Neumann", Jon then things may be a lot easier.
One approach may include this:
#define DELIM 'c'
ostringstream namestream;
infile.get(namestream, DELIM);
string name = namestream.str();
That will get the string from infile until the character DELIM is reached. Looking at your file, it looks like name parts and first/last names are separated by a space or comma followed by space, wherease the gap between the end of the name and the first number may be a tab. If that is the case, you can do this with tab ('\t') as your delimeter character.
Unfortunately, this approach only supports DELIM as a single character, not a set of characters. So you wouldn't be able to read up to the first digit (or "digiit or '-'" if you want to support negative numbers).
Here is a complete example program:
#include <iostream>
#include <string>
#include <locale>
#include <fstream>
template<
class charT,
class iter_type,
class string_iterator_type
>
void basic_get_name(iter_type beg, iter_type end, std::ios_base& str,
string_iterator_type it1, string_iterator_type it2)
{
auto ctypeFacet = &std::use_facet<std::ctype<charT>>(str.getloc());
typedef std::ctype_base base_type;
while (beg != end)
{
if (ctypeFacet->is(base_type::alpha | base_type::punct, *beg))
*it1++ = *beg;
else if (ctypeFacet->is(base_type::space, *beg))
{
if (ctypeFacet->is(base_type::alpha, *++beg))
{
while (ctypeFacet->is(base_type::alpha, *beg) && (beg != end))
*it2++ = *beg++;
break;
}
break;
}
++beg;
}
}
template<class charT>
void get_name(std::basic_istream<charT>& is, std::basic_string<charT>& first,
std::basic_string<charT>& last)
{
typedef std::istreambuf_iterator<charT> iter_type;
basic_get_name<charT>(iter_type{is}, iter_type{},
is, std::back_inserter(first),
std::back_inserter(last));
}
Here is how you would call it:
int main()
{
std::string first, last;
get_name(infile, first, last);
std::cout << first << last;
}
You can even go as far as to create your own class that has both first and last name strings and create a facet for the extraction of the names into those data members.
And get_name should return the stream so that a check can be made on the stream state. As it stands now this code does not do that but it can be implemented through an argument of type ios_base::iostate and adding stream state error bits to it inside basic_get_name.

Reading till the end of a line in C++

I have a text file like this :
Sting Another string 0 12 0 5 3 8
Sting Another string 8 13 2 0 6 11
And I want to count how many numbers are there. I think my best bet is to use while type cycle with a condition to end counting then another line starts but I do not know how to stop reading at the end of a line.
Thanks for your help in advance ;)
Split your input stream into lines
std::string line;
while (std::getline(input, line))
{
// process each line here
}
To split a line into words, use a stringstream:
std::istringstream linestream(line); // #include <sstream>
std::string word;
while (linestream >> word)
{
// process word
}
You can repeat this for each word to decide whether it contains a number. Since you didn't specify whether your numbers are integer or non-integers, I assume int:
std::istringstream wordstream(word);
int number;
if (wordstream >> number)
{
// process the number (count, store or whatever)
}
Disclaimer: This approach is not perfect. It will detect "numbers" at the beginning of words like 123abc, it will also allow an input format like string 123 string. Also this approach is not very efficient.
Why don't you use a getline()?
End of Line is represented by '\n' character.
Put a condition in your while loop to end when it encounters '\n'