What is actually the type of elements of std::string?
I am trying to read the elements from a stream, in the following
way:
std::string str = "";
int c;
// Read string size
int n;
is >> n;
// Read string
for(int i=0; i<n; i++)
{
c = is.get();
str += c;
}
This seems to work, but the compiler (C++ Builder 6.0)
issues a warning at
c = is.get();
The warning is: Conversion may lose significant digits
On the other hand, if I switch to "char" instead of "int"
it seems that the code does not work.
How to write the code that does exactly the same thing
but without the warning?
Leslaw
char.
>
>I am trying to read the elements from a stream, in the following
>way:
>
>std::string str = "";
>int c;
>
>// Read string size
>int n;
>is >> n;
>
>// Read string
>for(int i=0; i<n; i++)
> {
> c = is.get();
> str += c;
> }
>
>This seems to work, but the compiler (C++ Builder 6.0)
>issues a warning at
>c = is.get();
>The warning is: Conversion may lose significant digits
Are you sure the warning isn't at the following line? std::string
doesn't have an operator+= which takes an int, and int to char is a
conversion that might lose significance.
>
>On the other hand, if I switch to "char" instead of "int"
>it seems that the code does not work.
What do you mean by "does not work"?
>
>How to write the code that does exactly the same thing
>but without the warning?
>
Understand how istream::get() reports errors.
--
Richard Herring
> Are you sure the warning isn't at the following line? std::string doesn't
> have an operator+= which takes an int, and int to char is a conversion that
> might lose significance.
Well, I understand that, but the essential question is how to read
the elements of std::string correctly, if std::string contains char,
whereas get() returns int. This looks like some sort of incompatibility.
>>
> Understand how istream::get() reports errors.
Surely I am not asking how to cheat the compiler to not to get messages,
but how to do things correctly.
Leslaw
Do you understand *why* get() returns int, and what subset of possible
int values it can return? That's the point of my question below:
>>>
>> Understand how istream::get() reports errors.
>
>Surely I am not asking how to cheat the compiler to not to get messages,
>but how to do things correctly.
>
Good. Then you should be reading an int from get(), separating out the
values that represent characters from those which don't, appending the
chars to the string and doing something else with the non-chars.
--
Richard Herring
> Do you understand *why* get() returns int, and what subset of possible int
> values it can return? That's the point of my question below:
Yes, that's the point. I do not understand why get() returns int. If
get() is supposed to read successive bytes, then I would expect it
to return char. What are the cases when it returns int?
Is this EOF, or what else?
> Good. Then you should be reading an int from get(), separating out the values
> that represent characters from those which don't, appending the chars to the
> string and doing something else with the non-chars.
Do you mean casting from int to char? This removes the warning message
and from my brief test seems to work OK, but how can I be sure if
it always works correctly?
Leslaw
EOF. Read the documentation.
>
>> Good. Then you should be reading an int from get(), separating out
>>the values that represent characters from those which don't,
>>appending the chars to the string and doing something else with the
>>non-chars.
>
>Do you mean casting from int to char?
Or any other form of conversion.
>This removes the warning message
>and from my brief test seems to work OK, but how can I be sure if
>it always works correctly?
By only converting when you know that the value is in the range valid
for char.
--
Richard Herring
char. Specifically, the definition of std::string looks like:
typedef basic_string<char> string;
> I am trying to read the elements from a stream, in the following
> way:
>
> std::string str = "";
> int c;
>
> // Read string size
> int n;
> is >> n;
>
> // Read string
> for(int i=0; i<n; i++)
> {
> c = is.get();
> str += c;
> }
Hmm...I think I'd do this more like:
int n;
is >> n;
std::string str(n, ' ');
is.read(&str[0], n);
This should eliminate the warning(s), and if there's a difference in
efficiency, I'd guess it's more likely to favor this version than the
other. In theory, this isn't entirely portable -- the current
standard doesn't guarantee that std::string will use contiguous
storage for the characters. In reality, current implementations do
use contiguous storage, and the next version of the standard will
require it, so there's not really much chance of a new implementation
that uses non-contiguous storage.
--
Later,
Jerry.
> int n;
> is >> n;
What happens if there aren't n characters left to read? (And in
fact, I don't know. I practically never use istream::read, and
I don't have any documentation handy to check. But it's
obviously a case which has to be considered.)
More generally, considering the first algorithm: the
no-argument form of istream::get returns an int in order to
return out of band data, e.g. EOF. Once that data has been
tested for and excluded, the resulting value must be converted
to a char. Formally, a rather complex operation, since the
resulting value is in the range 0...UCHAR_MAX, which might not
fit into a char (and if the results don't fit, the behavior is
implementation defined, and might result in a signal).
Practically, implementations which use signed plain char will
always ensure that this works, given that it is such a standard
idiom (in C, at least). But if you really want to avoid it,
something like:
char ch;
// ...
if ( input.get( ch ) ) {
// succeeded, you can safely use ch...
} else {
// error or end of file, the contents of
// ch are unchanged.
}
--
James Kanze
[ ... ]
> > std::string str(n, ' ');
> > is.read(&str[0], n);
[ ... ]
> What happens if there aren't n characters left to read? (And in
> fact, I don't know. I practically never use istream::read, and
> I don't have any documentation handy to check. But it's
> obviously a case which has to be considered.)
It returns the stream, which will have its failbit set if the read
failed (i.e., if it couldn't read as many characters as requested).
Given that the file specified the length, chances are pretty good
that the proper reaction is to log the error and abort, but depending
on what data it's trying to read, it might be able to live with
partial input, request the input from another source, or who knows
what.
Unlike fread, however, istream::read does NOT indicate how many
characters were read, so if (for example) trailing blanks in the
input were significant, and you really needed to distinguish them
from the string's initial value, this method probably wouldn't work
well (unless you could initialize the string to some other value you
knew wouldn't come from the input).
--
Later,
Jerry.
istream::gcount() will tell you how many characters were read by the last
call to istream::read
/Leigh
Your insistence on describing the behaviour of rare corner case
implementations is unhelpful as it confuses the issue, the fact that char ch
= (unsigned char) foo; is implementation defined behaviour is not
justification for eschewing the use of istream::read(). Please rejoin the
real world.
/Leigh
I never said it was (and I do use istream::read and
istream::write). Still, a competent programmer will want to
understand what he's doing, and what the restrictions and the
trade-offs are.
> Please rejoin the real world.
In the real world, not every machine is a PC.
--
James Kanze
[ ... ]
> istream::gcount() will tell you how many characters were read by
> the last call to istream::read
Ah, quite right. I should have thought of that...
--
Later,
Jerry.