Using ifstream as fscanf - c++

Assume that I have an input as follows:
N (X_1,Y_1) (X_2,Y_2) .... (X_N, Y_N)
where N, X_i and Y_i are integers.
An example:
2 (55,1) (521,7)
To read this, I can do something like this(assume all variables are defined, etc.):
fscanf(fin,"%d ",&N);
for (int i = 0; i < N; i++)
fscanf(fin,"(%d,%d) ", &X[i], &Y[i]);
The question is, how can I do this easily using ifstream. I can get string's, and then I can get rid of nondigits and using stringstream I can get two numbers but this seems a bit cumbersome. Is there an easier, more elegant way?
Thanks

int n, x, y;
char c;
if (is >> n)
for (int i = 0; i < n; ++i)
if (is >> c && c == '(' &&
is >> x &&
is >> c && c == ',' &&
is >> y &&
is >> c && c == ')')
{
X[i] = x;
Y[i] = y;
}
else
throw std::runtime_error("invalid inputs");
You can simplify the all-important inner if condition above to...
is >> chlit('(') >> x >> chlit(',') >> y >> chlit(')')
...with a simple support type for consuming a specific character:
struct chlit
{
chlit(char c) : c_(c) { }
char c_;
};
inline std::istream& operator>>(std::istream& is, chlit x)
{
char c;
if (is >> c && c != x.c_)
is.setstate(std::iostream::failbit);
return is;
}
See a complete program illustrating this on ideone here.
An old post of mine did something similar for consuming specific strings. (The above chlit could be a template, but chlit<','>() is ugly to read and write - I'd rather trust the compiler).

cin >> N;
for (int i = 0; i < N; i++)
{
cin.ignore(100,'(');
cin >> X[i];
cin.ignore(100,',');
cin >> Y[i];
cin.ignore(100,')');
}
It can handle whitespaces also, as it can read input like:
2 ( 1 , 3 ) ( 5 , 6 )
Demonstration at ideone: http://www.ideone.com/hO0xG

Related

Extraction operator causing my program to exit?

I'm a usual lurker but this is my first post! I understand you guys like detail so I will do my best. I will appreciate whatever input anyone has.
I am working on an overloading the extraction operator for an object with a dynamic array of digits. The console input will have leading white space, then an int, then anything after. I need to ignore white space, extract the int, and then leave the rest alone. Easy right?
Here is an example of code I found online:
istream & operator >> (istream &m, MyInt & p)
{
int x = 0;
p.currentLength = 0;
while ((m.peek() == '\n') || (m.peek() == '\0') ||
(m.peek() == '\t') || (m.peek() == ' '))
{
m.get();
}
while ((m.peek() >= '0') && (m.peek() <= '9'))
{
if (p.currentLength >= p.maxSize)
{
p.grow();
}
m >> p.theNumber[x];
x++;
p.currentLength++;
}
m.get();
// reverse the order (i.e. - 123 to 321)
char * temp = new char[p.maxSize];
for (int y = 0; y < p.currentLength; y++)
{
temp[y] = p.theNumber[p.currentLength - 1 - y];
}
delete [] p.theNumber;
p.theNumber = temp;
return m;
}
Now, I understand this method may work, however to me, that seems like an extremmeelly inefficient method. For a trillion digit number, Grow() would reallocate the array a trillion times! Perhaps this is not as bad as I think it is?
My current method has been using seekg() and peek() and get(). Like so:
istream& operator >> (istream& is, MyInt& z)
{
int i = 0, j = 0;
// check if next char is white
while (is.peek() == 38)
{
j++;
is.seekg(j); // skip if white
}
while (isdigit(is.peek()))
{
i++;
is.seekg(j + i);
if (!is.peek())
{
is.clear();
break;
}
}
is.seekg(j);
z.length = i;
z.digits = new int[i + 1];
for (i = 0; i < z.length; i++)
{
z.digits[i] = C2I(is.get());
}
return is;
}
Also, here is my main:
int main()
{
MyInt B;
cout << "\n\nChange B to what num? ---> ";
cin >> B;
cout << "B is now: " << B;
char c;
cout << "\n\n\n\n\nEnter char to exit : ";
cin >> c;
return 0;
}
For the life of me I can not find what is causing my program to exit. The last output seems to say, 'B is now: -1'
I believe the this means the << B failed. I have B initialized to 0 currently, and the rest of my code has presented no other issues. It's private member data only include the pointer and a length (num of digits). Also C2I() is a function that converts '0' through '9' to 0 through 9.
A big issue for me is I am fairly new to parsing, so I don't have very eloquent ways to test this, or other ideas.
Again I appreciate everything you guys do. I have already learned a great deal from browsing here!

how to read characters and integers from a file?

So I have a data file that looks something like this:
x + y + z
30 45 50
10 20 30
The only characters I needed was the operators, so '+' '+' I was able to use file.get() to successfully get those characters and put them in an array. Problem is I need to get the next line of numbers, and assign them to the values x , y z . I know I cant use .get() , I would have to use getline. Will i have to eliminate the file.get() and use getline instead for first part also?
I looked at some of the questions posted on here but none of them were quite like mines. Note I'm actually going to be using these values for another part of my program, just used cout to see if my values were being read correctly
Here's my previous code:
main(int argc, char *argv[])
{
int a=0;
int n;
fstream datafile;
char ch;
pid_t pid;
int a, b, c, result;
string line;
datafile.open("data1.txt");
if(datafile)
{
for(int i=0; i <9; i++)
{
datafile.get(ch);
if (ch == '*'||ch == '/'||ch == '+'||ch == '-')
{
operations[a] = ch;
cout<<operations[a];
a++;
}
}
}
else
cout<<"Error reading file";
}
So this is how I was getting the first line of the file in the beginning. It worked like I wanted it to, may have not been the nicest coding but it worked. Nevertheless I tried to get the rest of the file, this time using getline, but instead of getting the numbers I was getting a bunch of random gibberish/numbers. I know if I use getline, the first line cannot be in my loop. I know this is how I would get the numbers.
while(getline(datafile, line))
{
istringstream ss(line);
ss >> x >> y >> z;
cout<<x<<""<<y<<""<<z;
}
Would the following make sense for the first line, or am I missing something:
string input;
std::getline(datafile, input)
for (int i = 0; i < input.size(); i++)
if (input[i] == '+' || ...)
{
operations[a] = input[i];
a++;
}
If you don't want to use getline, you could simply read the entire file stream (note that the bool is a rather naive way to handle the problem, I'd recommend something more elegant in your actual code):
bool first = true;
string nums;
int lines = 0;
vector<vector<int>> numlines;
vector<int> topush;
while (!datafile.eof())
{
char ch = datafile.get()
if (ch == 12 && first) //I don't know if '\n' is valid, I'd assume it is but here's the sure bet
first = false;
else if (first && (ch == '+' || ...))
{
operator[a] = ch;
a++;
}
else if (!first && (ch >= '0' && ch <= '9'))
{
if (!(datafile.peek() >= '0' && datafile.peek() <= '0'))
{
numlines[lines].push_back(atoi(nums.c_str());
nums.clear();
if (datafile.peek() == 12)
{
numlines.push_back(topush);
lines++;
}
}
else
nums = nums + ch;
}
Honestly, I can't be sure the above will work exactly right, I'd recommend you just modify your code to use getline exclusively. You'll need to add #include to get atoi.
Add this to your code:
while(!datafile.eof()){
string s;
getline(datafile, s);
istringstream in(s);
string tmp;
while(in >> tmp){
int i = stoi(tmp)
//Do something with i....
}
}

c++ parse string using multiple delimiters using inbuilt c/c++

I am trying to parse a string that looks like "1,4-6,8-10,12" and push_back the results into a vector of ints/char*. While parsing, if the logic comes across 4-6 then it should push the ints 4,5 and 6 in the vector. I am trying to do this using strtok but it modifies the only copy of the input string so I am not getting anywhere. I cannot use boost or else tokenizer would have been very easy and useful.
#include <stlport\sstream>
#include <stlport\vector>
using namespace std;
...
stringstream ss("1,4-6,8-10,12");
vector<int> v;
int x, x2;
char c;
while (ss >> x)
{
v.push_back(x);
if (!(ss >> c))
break; // end of input string
if (c == '-')
{
if (!(ss >> x2))
throw; // incorrect input string
for (int i = x+1; i <= x2; i++)
v.push_back(i);
if (!(ss >> c))
break; // end of input string
}
else if (c != ',')
throw; // incorrect input string
}
// check
int s = v.size();
// s = 8, v:{1,4,5,6,8,9,10,12}
std::stringstream ss("1,4-6,8-10,12");
std::vector<int> v;
int x;
while(ss >> x)
{
v.push_back(x);
char c;
ss >> c; //will just discard a non space char.
if(c != ',' || c != '-') ss.unget(); //... unless is just , or -
}
Time to write this: 1 minute.
Time to search for an appropriate algorithm function: 5 minutes at least.
Decide yourself what's more productive.

Reading backslash characters literally in c++

In my c++ code, I am trying to read \ and / characters literally, but \ is read as being same as /.
My code is this:
int x, y;
char orient;
cin >> N >> goalA >> goalB;
for (int i = 0; i < N; i++)
{
cin >> x >> y >> orient;
xVal [i] = x;
yVal [i] = y;
if (orient = '/')
{
orientVal [i] = 1;
}
else
{
orientVal [i] = 2;
}
cout << orientVal[i];
}
but even when orient = '\', I get orientVal [i] = 1 instead of 2. How can I fix this? Thanks.
An assignment is done with = and an equality with ==
So the statement
if (orient = '/')
should be
if (orient == '/')
The first statement always evaluates to true irrespective of what orient contains. Because in C/C++ a non zero value is True. Your assignment makes the statement to simply as
if ('/')
which is nothing but
if (true)
Because if (orient = '/') is an assignment that always evaluates to true as a boolean (non-zero).
You want if (orient == '/').
Line
if(orient = '/'
Should be
if ('/' == orient) ...

How do I create a check for this code?

I'm trying to make it so that the code checks if the user input is between (and including) 10 and 100.
Being so used to just single inputs, I'm having trouble since it's an array...
int main()
{
int numlist[20];
for(int i = 0; i < 20; i++)
{
cout << "Enter # " << i + 1 << " : ";
// here is where I am going wrong...
if ((numlist[i] <= 100) && (numlist[i] >= 10))
{
cin >> numlist[i];
}
}
}
Shouldn't you put the input statement cin >> numlist[i] before the test if ((numlist[i] <= 100) && (numlist[i] >= 10)) ?
it looks like you want to do something like this:
int temp = 0;
for (int i = 0; i < 20; i++)
{
cin >> temp;
if ((temp <= 100) && (temp >= 10))
numlist[i] = temp;
}
Just to give a slightly different way you could do this, you might consider a vector instead of an array, and read the data with an istream_iterator along with a standard algorithm:
std::vector<int> numlist;
std::remove_copy_if(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(numlist),
[](int i)->bool { return i<10 || i > 100; });
Edit: I guess since I'm using C++11 lambda, I could also use the C++11 copy_if, which expresses the intent a bit more directly:
std::copy_if(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(numlist),
[](int i)->bool { return i>=10 && i<=100; });
As far as "clever" goes, that's not the intent at all -- rather, what's desired is a simple, direct expression of the original intent: to copy (filtered) data from standard input to a container. It does take a bit to get used to the idea of treating files as containers (especially ones like std::cin, which is normally interactive), but ultimately a file is a sequence, and istream_iterator/ostream_iterator just let you treat them like other sequences.
As others have noted, you can't check a value that you haven't even read (from the user).
To constraint the input you must check the input after cin inside a do while loop, as long as it doesn't satisfy the constraint.
do
{
//you might cout here
cin >> numlist[i];
}
while ((numlist[i] > 100) || (numlist[i] < 10));