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.
Related
I am trying to read the last integer from an input such as-
100 121 13 ... 7 11 81
I'm only interested in the last integer and hence want to ignore all
previous integers.
I thought of using cin.ignore but that won't work here due to
unknown integers (100 is of 3 digits, while 13 is of 2 digits & so on)
I can input integer by integer using a loop and do nothing with them. Is there a better way?
It all depends on the use case that you have.
Reading a none specified number of integers from std::cin is not as easy at it may seem. Because, in contrast to reading from a file, you will not have an EOF condition. If you would read from a file stream, then it would be very simple.
int value{};
while (fileStream >> value)
;
If you are using std::cin you could try pressing CTRL-D or CTRL-Z or whatever works on your terminal to produce an EOF (End Of File) condition. But usually the approach is to use std::getline to read a complete line until the user presses enter, then put this line into a std::istringstream and extract from there.
Insofar, one answer given below is not that good.
So, next solution:
std::string line{};
std::getline(std::cin, line);
std::istringstream iss{line};
int value{};
while (iss >> value)
;
You were asking
Is there a better way?
That also depends a little. If you are just reading some integers, then please go with above approach. If you would have many many values, then you would maybe waste time by unnecessarily converting many substrings to integers and loose time.
Then, it would be better, to first read the complete string, then use rfind to find the last space in the string and use std::stoi to convert the last substring to an integer.
Caveat: In this case you must be sure (or check with more lines of code) that there are no white space at the end and the last substring is really a number. That is a lot of string/character fiddling, which can most probably avoided.
So, I would recommend the getline-stringstream approach.
You can try this simple solution for dynamically ignoring rest of the values except the last given in this problem as shown:
int count = 0;
int values, lastValue; // lastValue used for future use
std::cout << "Enter your input: ";
while (std::cin >> values) {
lastValue = values; // must be used, otherwise values = 0 when loop ends
count++;
}
std::cout << lastValue; // prints
Note: A character must be required to stop the while(), hence it's better put a . at last.
Output example
Enter your input: 3 2 4 5 6 7.
7
Try this:
for( int i=0; i<nums_to_ignore; i++) {
int ignored;
std::cin >> ignored;
}
my input file looks like this :
S New York 25 76 49
i want to read them where S is a character and New York is either a string or a cstring and the other 3 are integers. My problem is reading in New York i cant use getline since the 3 integers come after it and not in a new line. What can i do?
I would suggest using Regular Expressions to parse the input. Added to the standard library in C++ 11 <regex> C++ reference
More details on wikipedia: Regular Expressions in C++
Your other option is simply to read a character at a time and as long as the character isalpha() or isspace() followed by another isalpha(), store the character in your string. For example:
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main (void) {
char c, last = 0;
string s;
while (cin.get(c)) { /* read a character at a time */
/* if last is alpha or (last is space and current is alpha) */
if (last && (isalpha(last) || (isspace(last) && isalpha(c))))
s.push_back(last); /* add char to string */
last = c; /* set last to current */
}
cout << "'" << s << "'\n";
}
Example Use/Output
$ echo "S New York 25 76 49" | ./bin/cinget
'S New York'
It may not be as elegant as a regex, but you can always parse what you need simply by walking through each character of input and picking out what you need.
I have a text file that I need to read data from, and these data will go into various arrays later. The text file looks like:
1 5.154600E-05 1.329887E-02 1.907202E-03 3 -1 8937 8889 1 0 890 1532 1533
2 4.639140E-03 9.845286E-03 1.659781E-02 1 9708 0 0 1617
3 1.329887E-02 1.329887E-02 1.108239E-02 4 8497 5442 0 5711 0 1 1611 1619 889 1618
4 1.030920E-04 5.154600E-05 1.412360E-02 3 -1 6966 6965 1 0 888 1620 1330
5 6.030882E-03 6.546342E-03 1.030920E-04 2 8238 6002 0 0 1622 1621
6 9.484464E-03 5.154600E-05 4.072134E-03 2 6104 5455 0 0 2481 1112
1st, I need to take out specific column (in this case 5th column). The size of lines are not equal, and I couldn't break reading after catching 5th column. cin.ignore didn't help.
2nd, 5th column is always an integer, say N, and then (maybe in a separate function) I need to read the LAST N number of same line and store them into arrays. I have no clue how to do it.
Here is part of code:
while (!myfile.eof())
{
// myfile.ignore (50, '\n'); // This is just a try. 5th col. always ends at 50th charachter of line. It didn't work!
double a,b,c,d,e;
myfile >> a >> b >> c >> d >> e;
if (minsize>e)
minsize=e;
if (maxsize<e)
maxsize=e;
frequency2[int(e)]++;
}
Any help ?
You could use std::getline to get the whole line in each iteration, and then use std::istringstream to parse out the columns. Something like this:
std::string buffer;
std::getline(myfile, buffer);
std::istringstream iss(buffer);
// Do your stuff
double a, b, c, d, e;
iss >> a >> b >> c >> d >> e;
I'd recomming you using a string with some reversed size like 60 bytes.
Then read char by char. If the char is a White space/Line break and the string size > 0 you should handle the data in the string and clear the data in the string. If the size == 0 you should continue.
void handle(std::string& data)
{
if(data.size())
{
//Handle Input
data.clear();
}
}
std::string buf(60);
while(file.isgood())
{
char c = file.get();
if(!file.isgood())
break;
switch(c)
{
case '\n':
case ' '://Probably you need more cases here just debug to find out
handle(buf);
break;
default:
buf.push_back(c);
break;
}
}
To complete others solutions:
getline is the good way to read a file line by line
you could use (boost) tokenizer to build an iterable from the line
you could use (boost) lexical cast to parse data from this iterable
something like
while(file.good())
{
string line;
getline(file, line);
if(line.empty())
continue;
boost::tokenizer<> tok(line);
boost::tokenizer<>::iterator tokiter = tok.begin();
double ex1 = boost::lexical_cast<double>(*tokiter);
tokiter++;
float ex2 = boost::lexical_cast<float>(*tokiter);
//etc..
cout << ex1 << " " << ex2 << endl;
}
http://www.boost.org/doc/libs/1_55_0b1/libs/tokenizer/tokenizer.html
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_lexical_cast.html
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
}
}
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'