Reading numbers from a file

L

LuTHieR

Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder
6), and I'm doing it like this: first I use FileRead to store all the
data in the file to a char* variable (appropriately called 'data').
Then, I read every number using

char *ptr;
int value;

[...]

ptr = &data[position];
sscanf (ptr, "%i", &value);
position += IntToStr(value).Length();
numbers = value;

(All of it inside of a loop, of course).
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?
Big thanks,

LuTHieR
 
A

Alan Johnson

LuTHieR said:
Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder
6), and I'm doing it like this: first I use FileRead to store all the
data in the file to a char* variable (appropriately called 'data').
Then, I read every number using

char *ptr;
int value;

[...]

ptr = &data[position];
sscanf (ptr, "%i", &value);
position += IntToStr(value).Length();
numbers = value;

(All of it inside of a loop, of course).
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?
Big thanks,

LuTHieR


In C++ you should try avoid using the C I/O functions unless you have a
good reason to be using them. sscanf and its variants are notoriously
easy to use incorrectly. Here is an example of how to do the same using
C++'s stringstreams. Modify to your needs:

#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47" ;
std::istringstream ss(data) ;
int i ;
while (ss >> i)
std::cout << i << std::endl ;
}
 
L

LuTHieR

Thanks :)
One more question...if data contains a String before the actual data
(something like data[] = "label 1 2 3 4 5 6 7 8"), how can I easily
read it?
Thanks again

LuTHieR

Alan Johnson ha escrito:
LuTHieR said:
Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder
6), and I'm doing it like this: first I use FileRead to store all the
data in the file to a char* variable (appropriately called 'data').
Then, I read every number using

char *ptr;
int value;

[...]

ptr = &data[position];
sscanf (ptr, "%i", &value);
position += IntToStr(value).Length();
numbers = value;

(All of it inside of a loop, of course).
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?
Big thanks,

LuTHieR


In C++ you should try avoid using the C I/O functions unless you have a
good reason to be using them. sscanf and its variants are notoriously
easy to use incorrectly. Here is an example of how to do the same using
C++'s stringstreams. Modify to your needs:

#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47" ;
std::istringstream ss(data) ;
int i ;
while (ss >> i)
std::cout << i << std::endl ;
}
 
L

LuTHieR

Don't bother answering, it was as simple as using the >> operator to a
string type.
Thanks again

LuTHieR

LuTHieR ha escrito:
Thanks :)
One more question...if data contains a String before the actual data
(something like data[] = "label 1 2 3 4 5 6 7 8"), how can I easily
read it?
Thanks again

LuTHieR

Alan Johnson ha escrito:
LuTHieR said:
Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder
6), and I'm doing it like this: first I use FileRead to store all the
data in the file to a char* variable (appropriately called 'data').
Then, I read every number using

char *ptr;
int value;

[...]

ptr = &data[position];
sscanf (ptr, "%i", &value);
position += IntToStr(value).Length();
numbers = value;

(All of it inside of a loop, of course).
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?
Big thanks,

LuTHieR


In C++ you should try avoid using the C I/O functions unless you have a
good reason to be using them. sscanf and its variants are notoriously
easy to use incorrectly. Here is an example of how to do the same using
C++'s stringstreams. Modify to your needs:

#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47" ;
std::istringstream ss(data) ;
int i ;
while (ss >> i)
std::cout << i << std::endl ;
}
 
L

LuTHieR

In C++ you should try avoid using the C I/O functions unless you have a
good reason to be using them. sscanf and its variants are notoriously
easy to use incorrectly. Here is an example of how to do the same using
C++'s stringstreams. Modify to your needs:

#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47" ;
std::istringstream ss(data) ;
int i ;
while (ss >> i)
std::cout << i << std::endl ;
}

I must be really dumb :S Basing on the example you gave me, I was
trying to read many lines from the string, but on the second iteration
of the for loop, ss2 >> j evaluates to false, thus outputting just the
first line of the data string, and I can't figure out why. I guess the
problem is the use of the str method, but I don't know how to assign
the line variable to the istringstream in another way. Here's the code:


#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47" ;
char line[20];
int i,j;
std::istringstream ss(data);
std::istringstream ss2;

for (i=0; i<3; i++) {
ss.getline(line, 20);
ss2.str(line);
while (ss2 >> j)
std::cout << j << std::endl;
}
}

Thanks in advance.
 
A

Alan Johnson

LuTHieR said:
In C++ you should try avoid using the C I/O functions unless you have a
good reason to be using them. sscanf and its variants are notoriously
easy to use incorrectly. Here is an example of how to do the same using
C++'s stringstreams. Modify to your needs:

#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13 17 19 23 29 31 37 41 43 47" ;
std::istringstream ss(data) ;
int i ;
while (ss >> i)
std::cout << i << std::endl ;
}

I must be really dumb :S Basing on the example you gave me, I was
trying to read many lines from the string, but on the second iteration
of the for loop, ss2 >> j evaluates to false, thus outputting just the
first line of the data string, and I can't figure out why. I guess the
problem is the use of the str method, but I don't know how to assign
the line variable to the istringstream in another way. Here's the code:


#include <iostream>
#include <sstream>

int main()
{
char data[] = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47" ;
char line[20];
int i,j;
std::istringstream ss(data);
std::istringstream ss2;

for (i=0; i<3; i++) {
ss.getline(line, 20);
ss2.str(line);
while (ss2 >> j)
std::cout << j << std::endl;
}
}

Thanks in advance.

When the while loop ends, it is because ss2 has reached EOF (or end of
string, as the case may be), and its failbit is set. You need to clear
the failbit, which you can do by calling ss2.clear() at the beginning of
the for loop. However, you can avoid the issue completely by
constructing ss2 inside the for loop.

While I'm making suggestions, let's also get rid of the arbitrary number
20 for a line length, by using std::string and std::getline. Making
both of these changes, your code will look something like:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
std::string data = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47";
std::istringstream ss(data);
int j;

for (size_t i=0; i<3; i++)
{
std::string line;
std::getline(ss, line);
std::istringstream ss2(line);
while (ss2 >> j)
std::cout << j << std::endl;
}
}


Unless you have a reason, though, there is no need to process this on a
line by line basis. operator>> will skip newlines just like any other
whitespace, and you could reduce this to:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
std::string data = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47";
std::istringstream ss(data);
int j ;

while (ss >> j)
std::cout << j << std::endl ;
}
 
J

Jerry Coffin

@h76g2000cwa.googlegroups.com>, (e-mail address removed)
says...
Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder

[ ... ]
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?

[...and elsethread mentioned wanting to ignore other data
in the file]

You've gotten a number of replies, but I thought I'd add
one more way this could be done. At least as I understand
the situation, you only want to read the numbers from the
file, and ignore everything else.

One way to handle this would be to create a locale to
reflect that for your purposes, the file contains only
numbers (digits) and other "stuff" you're going to ignore
between the digits. From a viewpoint of reading the
numbers, everything is basically the same as whitespace
-- it separates one number from another, but otherwise
has no meaning. Therefore, we start by creating a ctype
facet that says it IS whitespace:

class number_only: std::ctype<char> {
number_only() : std::ctype<char>(get_table()) {}
static std::ctype_base::mask const *get_table() {
static std::ctype_base::mask *rc;

if ( rc == 0) {

// create a character classification table
rc = new std::ctype_base::mask
[std::ctype<char>::table_size];

// say that _everything_ is whitespace
std::fill_n(rc, std::ctype,char>::table_size,
std::ctype_base::space);

// except for [0-9]:
for (int i='0'; i<='9'; i++)
rc = std::ctype_base::digit;
]
return rc;
}
};

From there, we imbue the stream with a locale using that
facet, and we can just treat it as a file of numbers:

int main() {
std::ifstream x(wherever);

number_only n;
x.imbue(std::locale(std::locale(), n);

// now we can read in numbers, and everything
// else is ignored automagically.
std::vector<int> numbers;

// read in all the numbers:
std::copy(std::istream_iterator<int>(x),
std::istream_iterator<int>(),
std::back_inserter(numbers));

// just for grins, we'll display them, one per line:
std::copy(numbers.begin(), numbers.end(),
std::eek:stream_iterator<int>(std::cout, "\n"));

return 0;
}
 
L

LuTHieR

Alan said:
When the while loop ends, it is because ss2 has reached EOF (or end of
string, as the case may be), and its failbit is set. You need to clear
the failbit, which you can do by calling ss2.clear() at the beginning of
the for loop. However, you can avoid the issue completely by
constructing ss2 inside the for loop.

While I'm making suggestions, let's also get rid of the arbitrary number
20 for a line length, by using std::string and std::getline. Making
both of these changes, your code will look something like:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
std::string data = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47";
std::istringstream ss(data);
int j;

for (size_t i=0; i<3; i++)
{
std::string line;
std::getline(ss, line);
std::istringstream ss2(line);
while (ss2 >> j)
std::cout << j << std::endl;
}
}


Unless you have a reason, though, there is no need to process this on a
line by line basis. operator>> will skip newlines just like any other
whitespace, and you could reduce this to:

#include <iostream>
#include <sstream>
#include <string>

int main()
{
std::string data = "2 3 5 7 11 13\n 17 19 23 29 31\n 37 41 43 47";
std::istringstream ss(data);
int j ;

while (ss >> j)
std::cout << j << std::endl ;
}

Thanks once again. In fact, the line by line processing is required, I
need to know how many numbers have been read in every line and output
an error if there are more/less numbers than expected. Oh, and the line
length set to 20 was only for the example :)
Regards,

LuTHieR
 
L

LuTHieR

Jerry said:
@h76g2000cwa.googlegroups.com>, (e-mail address removed)
says...
Hi,
I'm reading a string of numbers from a file (using Borland C++ Builder

[ ... ]
This method seems to be working OK, but Borland CodeGuard tells me that
there's an access overrun in each sscanf call, so I guess there is a
better way of doing it. Could you please help me?

[...and elsethread mentioned wanting to ignore other data
in the file]

You've gotten a number of replies, but I thought I'd add
one more way this could be done. At least as I understand
the situation, you only want to read the numbers from the
file, and ignore everything else.

One way to handle this would be to create a locale to
reflect that for your purposes, the file contains only
numbers (digits) and other "stuff" you're going to ignore
between the digits. From a viewpoint of reading the
numbers, everything is basically the same as whitespace
-- it separates one number from another, but otherwise
has no meaning. Therefore, we start by creating a ctype
facet that says it IS whitespace:

class number_only: std::ctype<char> {
number_only() : std::ctype<char>(get_table()) {}
static std::ctype_base::mask const *get_table() {
static std::ctype_base::mask *rc;

if ( rc == 0) {

// create a character classification table
rc = new std::ctype_base::mask
[std::ctype<char>::table_size];

// say that _everything_ is whitespace
std::fill_n(rc, std::ctype,char>::table_size,
std::ctype_base::space);

// except for [0-9]:
for (int i='0'; i<='9'; i++)
rc = std::ctype_base::digit;
]
return rc;
}
};

From there, we imbue the stream with a locale using that
facet, and we can just treat it as a file of numbers:

int main() {
std::ifstream x(wherever);

number_only n;
x.imbue(std::locale(std::locale(), n);

// now we can read in numbers, and everything
// else is ignored automagically.
std::vector<int> numbers;

// read in all the numbers:
std::copy(std::istream_iterator<int>(x),
std::istream_iterator<int>(),
std::back_inserter(numbers));

// just for grins, we'll display them, one per line:
std::copy(numbers.begin(), numbers.end(),
std::eek:stream_iterator<int>(std::cout, "\n"));

return 0;
}

--
Later,
Jerry.

The universe is a figment of its own imagination.


Now that's a clever solution if I ever saw one. You are right, I only
want to read numbers from the file, but I also need to know if there is
anything *strange* within the file to warn the user that it might be
corrupted, so I'm afraid your solution might not work in this case.
However I found it a really interesting way to solve the problem, and
you can be sure that I'll save it and use it in the future :)
Thanks,

LuTHieR
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top