I have a bunch of input files that look like the following:
(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)
I need to write a function that parses these inputs one number at a time, so I need to be able to separate the input by numbers, eg: 8, then 7, then 15, then 0, another 0, so on.
The only way I've thought of so far is to use istream.get() which returns the next character's ASCII code, which I can convert back to its character format by casting it to char. Then I'd check if the character was a number or not (so the brackets are ignored) but this way, any double (or triple) digit numbers are only read one digit at a time.
What would be the best way to achieve this?
By the way, I must use istream. It's part of the specification that I'm not allowed to change
Thanks
This is one solution:
struct integer_only: std::ctype<char>
{
integer_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table()
{
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
std::fill(&rc['0'], &rc['9'+1], std::ctype_base::digit);
return &rc[0];
}
};
int main() {
std::cin.imbue(std::locale(std::locale(), new integer_only()));
std::istream_iterator<int> begin(std::cin);
std::istream_iterator<int> end;
std::vector<int> vints(begin, end);
std::copy(vints.begin(), vints.end(), std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
Input:
(8,7,15)
(0,0,1) (0,3,2) (0,6,3)
(1,0,4) (1,1,5)
Output:
8 7 15 0 0 1 0 3 2 0 6 3 1 0 4 1 1 5
Online demo : http://ideone.com/Lwx9y
In the above, you've to replace std::cin
with the file stream after opening the file successfully, as:
std::ifstream file("file.txt");
file.imbue(std::locale(std::locale(), new integer_only()));
std::istream_iterator<int> begin(file);
std::istream_iterator<int> end;
std::vector<int> vints(begin, end); //container of integers!
Here, vints
is a vector which contains all the integers. You would like work with vints
to do something useful. Also, you can use it where int*
is expected as:
void f(int *integers, size_t count) {}
f(&vints[0], vints.size()); //call a function which expects `int*`.
Similar trick can be applied when reading only words from a file. Here is an example:
Here's some code, you can adapt to meet your precise needs
for (;;)
{
int ch = in.get();
if (ch == EOF)
break;
if (isdigit(ch))
{
int val = ch - '0';
for (;;)
{
ch = in.get();
if (!isdigit(ch))
break;
val *= 10;
val += ch - '0';
}
// do something with val
}
}
This is untested code.
try to read a number. if that fails, clear error state and try to read a char
(and ignore it). repeat these two steps until reading a char fails, in which case you are at EOF or true failure.
it might be optimized by recognizing ')' and then reading until '('.
but i don't think it's worth it.
cheers & hth.,
Another solution:
#include <string>
#include <ostream>
#include <fstream>
#include <iostream>
struct triple
{
long a;
long b;
long c;
};
std::ostream& operator << (std::ostream& os, const triple& value)
{
return os << value.a << "/" << value.b << "/" << value.c;
}
int main()
{
std::ifstream stream("Test.txt");
if (!stream)
{
std::cout << "could not open the file" << std::endl;
}
std::string dummy;
triple value;
while (std::getline(stream, dummy, '(') >> value.a &&
std::getline(stream, dummy, ',') >> value.b &&
std::getline(stream, dummy, ',') >> value.c)
{
std::cout << value << std::endl;
}
}
int getFirstPos(const string& str)
{
int pos=0,PosHit=0;
bool bfind=false;
if((PosHit=str.find(','))!=string::npos){
if(!bfind) pos=PosHit;
pos=pos>PosHit?PosHit:pos;
bfind=true;
}
if((PosHit=str.find('('))!=string::npos){
if(!bfind) pos=PosHit;
pos=pos>PosHit?PosHit:pos;
bfind=true;
}
if((PosHit=str.find(')'))!=string::npos){
if(!bfind) pos=PosHit;
pos=pos>PosHit?PosHit:pos;
bfind=true;
}
return bfind?pos:string::npos;
}
void main()
{
ifstream ifile("C:\\iStream.txt");
string strLine;
vector<double> vecValue; //store the datas
while(getline(ifile,strLine)){
if(strLine.size()==0)
continue;
int iPos=0;
while((iPos=getFirstPos(strLine))!=string::npos)
strLine[iPos]=' ';
istringstream iStream(strLine);
double dValue=0;
while(iStream>>dValue)
vecValue.push_back(dValue);
}
//output the result!
vector<double>::iterator it;
for (it=vecValue.begin(); it!=vecValue.end() ; ++it){
cout<<setprecision(3)<<*it<<endl;
}
}
Nowadays, in the year 2022, we have more powerful language elements available.
We can use if statements with initializer, std::ifstream
which will open the file with its constructor and close it automatically, the std::istreambuf_iterator which makes it easy to read a complete file through the std::string
s range constructor , the regex library and especially the std::sregex_token_iterator
(see here ) and powerful algorithms from the algorithm_library , r.g. std::transform .
With the above mentioned language elements, we can come up with a very short and powerful solution for your task.
This will make life simpler. . .
Please see:
#include <iostream>
#include <fstream>
#include <sstream>
#include <iterator>
#include <regex>
#include <algorithm>
#include <vector>
const std::regex re{ R"(\d+)" };
int main() {
// Open file and check, if it could be opened
if (std::ifstream ifs{ "r:\\numbers.txt" }; ifs) {
// Read complete source file into string
std::string text(std::istreambuf_iterator<char>(ifs), {});
// Get numbers (digits only)
std::vector<int> numbers{};
std::transform(std::sregex_token_iterator(text.begin(), text.end(), re), {}, std::back_inserter(numbers), [](const std::string& s) {return stoi(s);});
// Do something with those numbers
// . . .
// Debug output
for (const int i : numbers) std::cout << i << ' ';
}
else std::cerr << "\n***Error: Could not open input file\n";
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.