View mode: basic / threaded / horizontal-split · Log in · Help
March 25, 2012
reading formatted strings: readf("%s", &stringvar)
I am trying to figure out the cause of my problem in the 
following post:

     
http://forum.dlang.org/thread/qfbugjkrerfboqhvjttw@forum.dlang.org

and encountered something peculiar about reading strings. 
Whenever a distinct terminator is indicated in the input format 
(ex. " %s@", @ being the terminator), readf() leaves the 
terminator on the input buffer after reading the data. If no 
terminator is specified the only way to indicate end of input is 
to use ctrl-d (ctrl-z on windows), however that causes the eof 
indicator to be set to true and the stream is marked as empty for 
all future attempts to access stdin.

void main(string[] args)
{
    string s1;
    double d;
    string s2;

    writeln("Enter a @ terminated string (multiline ok):");
    readf(" %s@", &s1);
    auto arr = s1.split();

    if (!stdin.eof()) {
        writeln("The stream is not empty.");
    } else {
        writeln("The stream is empty.");
    }
    writeln("Enter another string (terminated with 
cntrl-d|ctrl-z):");
    readf(" %s", &s2);  // No matter how many read attempts
                                // are made after this point, 
none of them will work.
                                // Insert \n after %s to see 
difference.

    writeln("Enter a decimal value:");
    readf(" %s", &d);
    //readf(" %s", &d);
    //readf(" %s", &d);
    //readf(" %s", &d);
    //readf(" %s", &d);

    if (!stdin.eof()) {
        writeln("The stream is not empty.");
    } else {
        writeln("The stream is empty.");
    }

    writeln("d = ", d);
    writeln("arr = ", arr);
    writeln("s = ", s2);
}

Is there no way to indicate eof without marking the stream 
(buffer?) as empty for all future uses after encountering 
cntrl-d/cntrl-z during string input? I would expect to be able to 
terminate string input with cntrl-d/cntrl-z without rendering the 
input stream inaccessible.

Thanks,
Andrew
March 25, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On 03/25/2012 06:00 AM, Tyro[17] wrote:
> I am trying to figure out the cause of my problem in the following post:
>
> http://forum.dlang.org/thread/qfbugjkrerfboqhvjttw@forum.dlang.org

Sorry that I missed your question there. :(

> and encountered something peculiar about reading strings. Whenever a
> distinct terminator is indicated in the input format (ex. " %s@", @
> being the terminator), readf() leaves the terminator on the input buffer
> after reading the data. If no terminator is specified the only way to
> indicate end of input is to use ctrl-d (ctrl-z on windows), however that
> causes the eof indicator to be set to true and the stream is marked as
> empty for all future attempts to access stdin.
>
> void main(string[] args)
> {
> string s1;
> double d;
> string s2;
>
> writeln("Enter a @ terminated string (multiline ok):");
> readf(" %s@", &s1);
> auto arr = s1.split();
>
> if (!stdin.eof()) {
> writeln("The stream is not empty.");
> } else {
> writeln("The stream is empty.");
> }
> writeln("Enter another string (terminated with cntrl-d|ctrl-z):");

I am not sure about the cntrl-d|ctrl-z part though. Since it terminates 
the input, the program should not be able to read any more characters.

> readf(" %s", &s2); // No matter how many read attempts

I advise reading string by readln(). You can call chomp() to get rid of 
whitespace around it:

    while (s2.length == 0) {
        s2 = chomp(readln());
    }

This has been my understanding of the matter: [1]

<quote>
There are surprises even when reading strings from the console.

Being character arrays, strings can contain control characters like '\n' 
as well. When reading strings from the input, the control character that 
corresponds to the Enter key that is pressed at the end of console input 
becomes a part of the string as well. Further, because there is no way 
to tell readf() how many characters to read, it continues to read until 
the end of the entire input. For these reasons, readf() does not work as 
intended when reading strings:
</quote>

> // are made after this point, none of them will work.
> // Insert \n after %s to see difference.
>
> writeln("Enter a decimal value:");
> readf(" %s", &d);
> //readf(" %s", &d);
> //readf(" %s", &d);
> //readf(" %s", &d);
> //readf(" %s", &d);
>
> if (!stdin.eof()) {
> writeln("The stream is not empty.");
> } else {
> writeln("The stream is empty.");
> }
>
> writeln("d = ", d);
> writeln("arr = ", arr);
> writeln("s = ", s2);
> }
>
> Is there no way to indicate eof without marking the stream (buffer?) as
> empty for all future uses after encountering cntrl-d/cntrl-z during
> string input? I would expect to be able to terminate string input with
> cntrl-d/cntrl-z without rendering the input stream inaccessible.
>
> Thanks,
> Andrew

With the change above (and assuming that we don't end the stream), the 
rest of the reads succeed for me.

Ali

[1] http://ddili.org/ders/d.en/strings.html
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
First of all thank your very much for your assistance.

On Sunday, 25 March 2012 at 15:04:30 UTC, Ali Çehreli wrote:
> On 03/25/2012 06:00 AM, Tyro[17] wrote:
> > I am trying to figure out the cause of my problem in the
> following post:
> >
> > 
> http://forum.dlang.org/thread/qfbugjkrerfboqhvjttw@forum.dlang.org
>
> Sorry that I missed your question there. :(
>
> > and encountered something peculiar about reading strings.
> Whenever a
> > distinct terminator is indicated in the input format (ex. "
> %s@", @
> > being the terminator), readf() leaves the terminator on the
> input buffer
> > after reading the data. If no terminator is specified the
> only way to
> > indicate end of input is to use ctrl-d (ctrl-z on windows),
> however that
> > causes the eof indicator to be set to true and the stream is
> marked as
> > empty for all future attempts to access stdin.
> >
> > void main(string[] args)
> > {
> > string s1;
> > double d;
> > string s2;
> >
> > writeln("Enter a @ terminated string (multiline ok):");
> > readf(" %s@", &s1);
> > auto arr = s1.split();
> >
> > if (!stdin.eof()) {
> > writeln("The stream is not empty.");
> > } else {
> > writeln("The stream is empty.");
> > }
> > writeln("Enter another string (terminated with
> cntrl-d|ctrl-z):");
>
> I am not sure about the cntrl-d|ctrl-z part though. Since it 
> terminates the input, the program should not be able to read 
> any more characters.
>
> > readf(" %s", &s2); // No matter how many read attempts
>
> I advise reading string by readln(). You can call chomp() to 
> get rid of whitespace around it:
>
>     while (s2.length == 0) {
>         s2 = chomp(readln());
>     }

You can achieve the same with:

     readf(" %s\n", &s2);

My goal however, is not to read one line of information. Rather,  
it is to
read multiple lines of information from standard input. I get 
close to
being able to do so if i don't including "\n" as a part of my 
format string
or if I changing your suggestion to

     while (!stdin.eol()) {
         s2 = chomp(readln());
     }

but again I run into the predicament was before, a need to close 
the
the stream with Ctrl-D/Ctrl-Z.

Andrew
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
First of all thank your very much for your assistance.

On Sunday, 25 March 2012 at 15:04:30 UTC, Ali Çehreli wrote:
> On 03/25/2012 06:00 AM, Tyro[17] wrote:
> > I am trying to figure out the cause of my problem in the
> following post:
> >
> > 
> http://forum.dlang.org/thread/qfbugjkrerfboqhvjttw@forum.dlang.org
>
> Sorry that I missed your question there. :(
>
> > and encountered something peculiar about reading strings.
> Whenever a
> > distinct terminator is indicated in the input format (ex. "
> %s@", @
> > being the terminator), readf() leaves the terminator on the
> input buffer
> > after reading the data. If no terminator is specified the
> only way to
> > indicate end of input is to use ctrl-d (ctrl-z on windows),
> however that
> > causes the eof indicator to be set to true and the stream is
> marked as
> > empty for all future attempts to access stdin.
> >
> > void main(string[] args)
> > {
> > string s1;
> > double d;
> > string s2;
> >
> > writeln("Enter a @ terminated string (multiline ok):");
> > readf(" %s@", &s1);
> > auto arr = s1.split();
> >
> > if (!stdin.eof()) {
> > writeln("The stream is not empty.");
> > } else {
> > writeln("The stream is empty.");
> > }
> > writeln("Enter another string (terminated with
> cntrl-d|ctrl-z):");
>
> I am not sure about the cntrl-d|ctrl-z part though. Since it 
> terminates the input, the program should not be able to read 
> any more characters.
>
> > readf(" %s", &s2); // No matter how many read attempts
>
> I advise reading string by readln(). You can call chomp() to 
> get rid of whitespace around it:
>
>     while (s2.length == 0) {
>         s2 = chomp(readln());
>     }

You can achieve the same with:

     readf(" %s\n", &s2);

My goal however, is not to read one line of information. Rather,  
it is to
read multiple lines of information from standard input. I get 
close to
being able to do so if i don't including "\n" as a part of my 
format string
or if I changing your suggestion to

     while (!stdin.eol()) {
         s2 = chomp(readln());
     }

but again I run into the predicament was before, a need to close 
the
the stream with Ctrl-D/Ctrl-Z.

Andrew
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On 3/26/12 5:55 AM, Tyro[17] wrote:
> You can achieve the same with:
>
> readf(" %s\n", &s2);
>
> My goal however, is not to read one line of information. Rather, it is to
> read multiple lines of information from standard input. I get close to
> being able to do so if i don't including "\n" as a part of my format string
> or if I changing your suggestion to
>
> while (!stdin.eol()) {
> s2 = chomp(readln());
> }
>
> but again I run into the predicament was before, a need to close the
> the stream with Ctrl-D/Ctrl-Z.

I made the decision for the current behavior while implementing readf. 
Basically I tried to avoid what I think was a mistake of scanf, i.e. 
that of stopping string reading at the first whitespace character, which 
is fairly useless.

Over the years scanf was improved with %[...] which allows reading 
strings with any characters in a set.

Anyway, if I understand correctly, there's no way to achieve what you 
want unless you read character-by-character and define your own control 
character. There's no out-of-band character that means "end of this 
input, but not that of the file".


Andrei
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On 03/26/2012 04:55 AM, Tyro[17] wrote:

>> > void main(string[] args)
>> > {
>> > string s1;
>> > double d;
>> > string s2;
>> >
>> > writeln("Enter a @ terminated string (multiline ok):");
>> > readf(" %s@", &s1);
>> > auto arr = s1.split();
>> >
>> > if (!stdin.eof()) {

Ah! That's one of the problems. stdin has no idea whether there are more 
characters available. eof() being true is not dependable unless an 
attempt to read a character is made and failed. This is the case in C 
and C++ as well.

>> > writeln("The stream is not empty.");
>> > } else {
>> > writeln("The stream is empty.");
>> > }
>> > writeln("Enter another string (terminated with
>> cntrl-d|ctrl-z):");
>>
>> I am not sure about the cntrl-d|ctrl-z part though. Since it
>> terminates the input, the program should not be able to read any more
>> characters.

I would like to repeat: ending the stream is not a solution because you 
want to read more data.

>>
>> > readf(" %s", &s2); // No matter how many read attempts

That's the actual problem, and ironically is already known to you. :) 
Use a \n at the end of that format string.

>>
>> I advise reading string by readln(). You can call chomp() to get rid
>> of whitespace around it:
>>
>> while (s2.length == 0) {
>> s2 = chomp(readln());
>> }
>
> You can achieve the same with:
>
> readf(" %s\n", &s2);

Thank you. However, that method does not remove trailing whitespace.

> My goal however, is not to read one line of information. Rather, it is to
> read multiple lines of information from standard input. I get close to
> being able to do so if i don't including "\n" as a part of my format 
string
> or if I changing your suggestion to
>
> while (!stdin.eol()) {
> s2 = chomp(readln());
> }
>
> but again I run into the predicament was before, a need to close the
> the stream with Ctrl-D/Ctrl-Z.

If I understand you correctly, the following program works for me:

import std.stdio;
import std.string;

void main(string[] args)
{
    string s1;
    double d;
    string s2;

    writeln("Enter a @ terminated string (multiline ok):");
    readf(" %s@", &s1);
    auto arr = s1.split();

    writeln("Enter a line of string:");
    readf(" %s\n", &s2);

    writeln("Enter a decimal value:");
    readf(" %s", &d);

    writeln("d = ", d);
    writeln("arr = ", arr);
    writeln("s = ", s2);
}

Ali
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On Monday, 26 March 2012 at 14:41:41 UTC, Andrei Alexandrescu 
wrote:
> On 3/26/12 5:55 AM, Tyro[17] wrote:
>> You can achieve the same with:
>>
>> readf(" %s\n", &s2);
>>
>> My goal however, is not to read one line of information. 
>> Rather, it is to
>> read multiple lines of information from standard input. I get 
>> close to
>> being able to do so if i don't including "\n" as a part of my 
>> format string
>> or if I changing your suggestion to
>>
>> while (!stdin.eol()) {
>> s2 = chomp(readln());
>> }
>>
>> but again I run into the predicament was before, a need to 
>> close the
>> the stream with Ctrl-D/Ctrl-Z.
>
> I made the decision for the current behavior while implementing 
> readf. Basically I tried to avoid what I think was a mistake of 
> scanf, i.e. that of stopping string reading at the first 
> whitespace character, which is fairly useless.

Couldn't the state of stdin be checked upon entrance into readf
and reopened if it is already closed?

Wouldn't that accomplish the desired effect while avoiding
the pitfalls of scanf?

> Over the years scanf was improved with %[...] which allows 
> reading strings with any characters in a set.
>
> Anyway, if I understand correctly, there's no way to achieve 
> what you want unless you read character-by-character and define 
> your own control character. There's no out-of-band character 
> that means "end of this input, but not that of the file".
>
>
> Andrei
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On Monday, 26 March 2012 at 17:34:37 UTC, Ali Çehreli wrote:
> On 03/26/2012 04:55 AM, Tyro[17] wrote:
>
> >> > readf(" %s", &s2); // No matter how many read attempts
>
> That's the actual problem, and ironically is already known to 
> you. :) Use a \n at the end of that format string.

Thanks. I'le use chomp(readln()) in the future.

> >>
> >> I advise reading string by readln(). You can call chomp() to
> get rid
> >> of whitespace around it:
> >>
> >> while (s2.length == 0) {
> >> s2 = chomp(readln());
> >> }
> >
> > You can achieve the same with:
> >
> > readf(" %s\n", &s2);
>
> Thank you. However, that method does not remove trailing 
> whitespace.
>
> > My goal however, is not to read one line of information.
> Rather, it is to
> > read multiple lines of information from standard input. I get
> close to
> > being able to do so if i don't including "\n" as a part of my
> format string
> > or if I changing your suggestion to
> >
> > while (!stdin.eol()) {
> > s2 = chomp(readln());
> > }
> >
> > but again I run into the predicament was before, a need to
> close the
> > the stream with Ctrl-D/Ctrl-Z.
>
> If I understand you correctly, the following program works for 
> me:
>
> import std.stdio;
> import std.string;
>
> void main(string[] args)
> {
>     string s1;
>     double d;
>     string s2;

Actually the problem is right here:

>     writeln("Enter a @ terminated string (multiline ok):");
>     readf(" %s@", &s1);
>     auto arr = s1.split();

I don't want to provide an explicit terminator, but instead
rely on Ctrl-D/Ctrl-Z to do the job while being able to
continue processing read request. As explained by Andrei,
this is not possible. But in my mind if the stdin stream
can be opened once, it can be opened again. What is the
negative effect of testing if it is closed and reopening it
on entering readf? Especially since there is a unique
implementation of readf to deal with input from stdin.

What is wrong with implementing reopen() in File for
specific use with stdin and then implementing readf
like this:

uint readf(A...)(in char[] format, A args)
{
    if(stdin.eof) stdin.reopen();
    return stdin.readf(format, args);
}

Andrew
March 26, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On 03/26/2012 02:12 PM, Tyro[17] wrote:

> I don't want to provide an explicit terminator, but instead
> rely on Ctrl-D/Ctrl-Z to do the job while being able to
> continue processing read request. As explained by Andrei,
> this is not possible. But in my mind if the stdin stream
> can be opened once, it can be opened again. What is the
> negative effect of testing if it is closed and reopening it
> on entering readf? Especially since there is a unique
> implementation of readf to deal with input from stdin.
>
> What is wrong with implementing reopen() in File for
> specific use with stdin and then implementing readf
> like this:
>
> uint readf(A...)(in char[] format, A args)
> {
> if(stdin.eof) stdin.reopen();
> return stdin.readf(format, args);
> }
>
> Andrew

That doesn't fit the way standard input and output streams work. These 
streams are bound to the application from the environment that has 
started them. The program itself does not have a way of manipulating how 
these streams are ended or connected.

Imagine that your program's stdin if piped from the output of another 
process:

  other | yours

Once 'other' finishes with its output, that's the end of the input of 
'yours'. 'yours' cannot communicate to the environment that it would 
like to continue reading more.

What you are asking for could be achieved only if both the environment 
and the program agreed that this would be the case.

Maybe I am missing something but that has been standard on many 
environments.

Ali
March 27, 2012
Re: reading formatted strings: readf("%s", &stringvar)
On 3/26/12 2:52 PM, Tyro[17] wrote:
> Couldn't the state of stdin be checked upon entrance into readf
> and reopened if it is already closed?

That won't work.

> Wouldn't that accomplish the desired effect while avoiding
> the pitfalls of scanf?

I don't think this is a pitfall. Essentially you don't have a definition 
of what constitutes a chunk of input. Once you get that define, you 
should be able to express it more or less easily.


Andrei
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home