c++ how to read stream till end of line - c++

I want to read an input like this from file
sphere 3 2 3 4
pyramid 2 3 4 12 3 5 6 7 3 2 4 1 2 3
rectangle 2 3 4 1 9 12
I want to do something like this
char name[64];
int arr[12];
ifstream file (..);
while(file)
{
file >> name;
while( //reach end of line)
file >> arr[i]
}
As you can see I don't know how many integers will be entered, that's why I want to stop at new line. I did it with getline, and then splitting the line, but they told me it can be done only with >> operator.
Note: I can't use std::string or std::vector.

The simple version is to use a manipulator similar to std::ws but instead of skipping all whitespace setting std::ios_base::failbit when a newline is encountered. This manipulator would then be used to instead of skipping whitespace implicitly whitespace other than newlines are skipped. For example (the code isn't test but I think something like this with the bugs and compilation errors removed should work):
std::istream& my_ws(std::istream& in) {
std::istream::sentry kerberos(in);
while (isspace(in.peek())) {
if (in.get() == '\n') {
in.setstate(std::ios_base::failbit);
}
}
return in;
}
// ...
char name[64];
int array[12];
while (in >> std::setw(sizeof(name)) >> name) { // see (*) below
int* it = std::begin(array), end = std::end(array);
while (it != end && in >> my_ws >> *it) {
++it;
}
if (it != end && in) { deal_with_the_array_being_full(); }
else {
do_something_with_the_data(std::begin(array), it);
if (!in.eof()) { in.clear(); }
}
}
My personal guess is that the assignment asked for reading the values into char arrays followed by converting them using atoi() or strol(). I think that would be a boring solution to the exercise.
(*) Never, not even in exmaple code, use the formatted input operator with a char array array without also setting the maximum allowed size! The size can be set by setting the stream's width(), e.g., using the manipulator std::setw(sizeof(array)). If the width() is 0 when using the formatted input operator with a char array, an arbitrary number of non-whitespace characters is read. This can easily overflow the array and become a security problem! Essentially, this is the C++ way of spelling C's gets() (which is now removed from both the C and the C++ standard libraries).

I suppose that you can use peek method:
while (file)
{
file >> name;
int i = 0;
while(file.peek() != '\n' && file.peek() != EOF) {
file >> arr[i++];
}
}

Related

Reading spaces from file

I have the following code which reads text from a file and stores the characters in a vector. However this code is not reading spaces and pushing them in the vector. I tried to use myRf>>noskipws but its not working.
int a;
int b;
int outp;
if (myRF.is_open())
{
while (!myRF.eof())
{
myRF >> a;
myRF >> b;
// myRf>>noskipws
for (int i=0; i<a; i++)
{
vector <char> col;
for (int j=0; j<b; j++)
{
myRF>>outp;
col.push_back(outp);
}
grid.push_back(col);
}
}
}
myRF.close();
When you enable std::noskipws leading whitespace isn't skipped. However, you try to read an int which can't start with a space! You should read a variable of type char to read, well, chars. That should just work.
Note that it is much faster to read chars using std::istreambuf_iterator<char>:
std::istream::kerberos(myRF);
if (kerberos) {
std::istreambuf_iterator<char> it(myRF, true), end;
while (it = end /* && other condition */) {
char c = *it;
++it;
// do other stuff
}
}
BTW, do not myRF.eof() to control the loop! That doesn't work because the stream cannot predict what you will try to read! The eof() member is only useful to determine why a read failed and distinguish between legit reason (have reached te end of the file) and broken input. Instead, read and check the result, e.g.
while (myRF >> a >> b) {
// ...
}
The problem is that the >> operator uses white space to determine when to end the stream extraction. If you want to grab every character from a file and store the separately then you would use something like this:
std::vector<char> letters;
std::ifstream fin ("someFile.txt");
char ch;
while (fin.get(ch))
letters.push_back(ch);

While not new line

if i have a file like
1 5 6 9 7 1
2 5 4 3 8 9
3 4 3 2 1 6
4 1 3 6 5 4
and i want to sort the numbers in every line ..
how to know when there is a newline ?
code for example :
while (!input.eof) {
input>>num;
while (not new line ?){
input>>o;
b.push_back(o);
}
sort (b.begin(),b.end());
se=b.size();
output<<num<<" "<<b[se-1]<<endl;
b.clear();
}
Note: i tried while(input>>num) and getline will now work with me
any ideas ?
Your input doesn't work! Using a loop testing for stream.eof() as the only control for the input is always wrong. You always need to test your input after you tried to read it. Incidentally, I posted earlier how you can guarantee that there is no newline between to objects. there is already an answer using std::getline() as a first stage which is somewhat boring. Here is an alternate approach:
std::istream& noeol(std::istream& in) {
for (int c; (c = in.peek()) != std::char_traits<char>::eof()
&& std::isspace(c); in.get()) {
if (c == '\n') {
in.setstate(std::ios_base::failbit);
}
}
return in;
}
// ...
while (input >> num) {
do {
b.push_back(num);
} while (input >> noeol >> num);
std::sort (b.begin(),b.end());
se=b.size();
output<<num<<" "<<b[se-1]<<endl;
b.clear();
input.clear();
}
You can use std::getline together with std::istringstream to read the file line by line, and then process each line individually:
#include <sstream>
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
int a, b;
//You can now read the numbers in the current line from iss
}
For further reference on how to read the file line by line, see this post.

Strings in C++: Problems with good() and get()

I am trying to read from a file. The code I used is
ifstream is;char c;
is.open("text.txt");
while(is.good() && !isdigit(is.peek()))
{ is.get(c)
word+=c;
}
The problem is that the last character is read twice (why?)
For e.g. if the word in the file is pink
the value of word becomes pinkk after the loop
Please suggest a solution
You always want to check that input was successful after you tried to read it. You are checking first when the stream has no idea what kind of value is going to be read. If you want to use peek() you should probably test against std::char_traits<char>::eof() first, e.g.:
for (std::char_traits<char>::int_type c;
std::char_traits<char>::eof() != (c = in.peek())
&& !std::isdigit(static_cast<unsigned char>(c); ) {
...
}
In your setup I would personally use std::istreambuf_iterator<char> as it is a lot easier, actually:
for (std::istreambuf_iterator<char> it(in), end;
it != end && !std::isdigit(static_cast<unsigned char>(*it); ++it) {
word += *it;
}
Note that char may be unsigned but std::isdigit() requires a positive value. If char is signed using my second name typically causes undefined behavior. To avoid this problem the char pass to std::isdigit() should be cast to unsigned char first.
Use the get() once more inside the loop to check whether its some character or not:
here is the code
while(is.good() && !isdigit(is.peek()))
{
is.get(c);
word+=c;
if(is.get(c))
{
is.seekg(-1,ios::cur) //move back get pointer if its not end of file
}
}
How about:
#include<cctype>
#include<fstream>
#include<iostream>
#include<string>
int main() {
std::ifstream fp("text.txt");
std::string word;
char c;
// while I am able to read a char...
while(fp>>c) {
//... if the char is a digit, stop reading...
if(std::isdigit(c))
break;
//... otherwise append it to my word string
word += c;
}
// close your files (or learn about scope-based destruction)
fp.close();
// print the resulting word
std::cout<<word<<std::endl;
return 0;
}
Compile: g++ example.cpp
Sample input (text.txt):
a
b
c
d
e
f
8
Sample output:
abcdef
The problem is that is.good() doesn't become false until AFTER you've had a failed read. So after you read the last character, is.good() is still true and you loop again to read another character (which fails), so you append the same character again.
To avoid this, you need to call is.good() AFTER reading (or peeking) at the next character -- if it is false then, there is no next character:
ifstream is;char c;
is.open("text.txt");
while(!isdigit(is.peek()) && is.good())
{ is.get(c)
word+=c;
}
or the simpler and equivalent:
ifstream is;char c;
is.open("text.txt");
while (is >> c && !isdigit(c))
word+=c;

Writing stream `operator>>` for serial number?

I have a serial number class of the following form:
class SerialNumber { ... }
and I want to write the operator>> for it:
istream& operator>>(istream& i, SerialNumber& s)
{
???
return i;
}
The serial numbers are always 19 characters long and start with a hex digit.
I am confused if I should istream.read 19 characters. It may include prefix whitespace. ?
Or whether I should read a i >> std::string and then check that it is 19 characters long. When you read a std::string it skips whitespace (is there a standard way to implement that?) Further if I read a std::string it may have a valid 19 character serial number prefix, and I may have "over-read" the input. ?
Update:
inline istream& operator>>(istream& is, SerialNumber& id)
{
ostringstream os;
is >> ws;
for (int i = 0; i < 19; i++)
{
char c;
is >> c;
os << c;
}
id = DecodeId(os.str());
return is;
}
Partially sanitized version of Dietmar Kühl code:
istream& operator>> (istream& in, SerialNumber& sn)
{
constexpr size_t n = 19;
istream::sentry se(in);
if (!se)
return in;
istreambuf_iterator<char> it(in.rdbuf()), end;
if (it == end || !isxdigit(*it))
{
in.setstate(ios_base::failbit);
return in;
}
string s(n,'?');
for (size_t i = 0; it != end && i < n && !isspace(char(*it)), ++i)
s[i] = *it++;
sn = DecodeId(s);
if (failed to decode)
in.setstate(ios_base::failbit);
return in;
}
The standard formatted input functions always follow the same pattern:
They start off with constructing a std::sentry object which handles any skipping of leading whitespace depending on the setting of the std::ios_base::skipws formatting flag.
The read value is unchanged if reading the value fails in any way and std::ios_base::failbit gets set.
Characters are consumed up to the first character which fails to match the format.
That is, the input function would look something like that:
std::istream& operator>> (std::istream& in, SerialNumber& s) {
std::istream::sentry kerberos(in);
if (kerberos) {
std::istreambuf_iterator<char> it(in.rdbuf()), end;
char buffer[20] = {};
int i(0);
if (it != end && std::isxdigit(static_cast<unsigned char>(*it))) {
for (; it != end && i != 19
&& !std::isspace(static_cast<unsigned char>(*it)); ++i) {
buffer[i] = *it++;
}
}
if (i == 19) {
SerialNumber(buffer).swap(s);
}
else {
in.setstate(std::ios_base::failbit);
}
}
return in;
}
You should do it one step at a time:
If you want to always skip whitespace, then start by doing i >> std::ws. The stream may not have the skipws flag set. Otherwise let the user decide whether to skip whitespace or not, and set the stream error bit when reading a whitespace.
Read the first char, see if its an hexadecimal digit. If its not, then set the stream error bit.
Read the rest of the 18 characters, and as soon as you find a character that does not meet the serial number format set the stream error bit.
You should disable skipws for this, otherwise you will get valid results from characters separated by whitespace. If you do, then make sure to restore the skipws flag when exiting the function (which may happen via an exception when setting the error bit, if exceptions are enabled on the stream).

Reading text file per line in C++, with unknown line length

I have a text file, that is formatted somewhat like this:
1 3 4 5 6
6 7 8
4 12 16 17 18 19 20
20
0
A line can contain 1 to 10000 integers. What I need to do, is read all of them line by line.
Pseudocode like this:
line=0;
i=0;
while(!file.eof()){
while(!endLine){
array[0][i++]=file.readChar();
}
line++;i=0;
}
So, I have an array , into which I would like to read every line, and each line would consist of each of these integers.
The problem I'm having, is how to check if the end of a line has come.
Note, I can't use strings.
Yes, This is for a homework, but the main task for the assignment is to build a tree and then transform it. I can do that, but I've no idea how to read the integers from the file.
Probably something like this:
after reading an int, I manually skip spaces, tabs, carriage return and end of line (for this one you'll have to implement your logic).
To read an int I read it directly using the C++ functions of ifstream. I don't read it character by character and then recompose it as a string :-)
Note that I skip \r as "spaces. The end of line for me is \n.
#include <iostream>
#include <fstream>
#include <vector>
int main()
{
std::ifstream file("example.txt");
std::vector<std::vector<int>> ints;
bool insertNewLine = true;
int oneInt;
//The good() here is used to check the status of
//the opening of file and for the failures of
//peek() and read() (used later to skip characters).
while (file.good() && file >> oneInt)
{
if (insertNewLine)
{
std::vector<int> vc;
ints.push_back(vc);
//With C++11 you can do this instead of the push_back
//ints.emplace_back(std::vector<int>());
insertNewLine = false;
}
ints.back().push_back(oneInt);
std::cout << oneInt << " ";
int ch;
while ((ch = file.peek()) != std::char_traits<char>::eof())
{
if (ch == ' '|| ch == '\t' || ch == '\r' || ch == '\n')
{
char ch2;
if (!file.read(&ch2, 1))
{
break;
}
if (ch == '\n' && !insertNewLine)
{
std::cout << std::endl;
insertNewLine = true;
}
}
else
{
break;
}
}
}
//Here we should probably check if we exited for eof (good)
//or for other file errors (bad! bad! bad!)
return 0;
}
There is a function called getline() which will read a whole line. Link
You need a function to read a value from a file or indicates an end of line or end of file condition, something like:
result_type GetNextValue (input_file, &value)
{
if next thing in file is a number, set value and return number_type
if next thing in file is an end of line, return end_of_line_type
if end of file found, return end_of_file_type
}
and then your array building loop becomes:
line = 0
item = 0
eof = false
while (!eof)
{
switch (GetNextValue (input_file, value))
{
case value_type:
array [line][item++] = value
case end_of_line_type:
line++;
item = 0;
case end_of_file_type:
eof = true
}
}
I'll leave the details to you as it's homework.
You could read the numbers in a char and check against carriage return. A snippet that I had just tried is given below:
ifstream ifile;
ifile.open("a.txt");
char ch;
while((ch = ifile.get()) != EOF)
{
std::cout<<ch<<"\n";
if (ch == '\n')
std::cout<<"Got New Line";
}
ifile.close();