March 25, 2012
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
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
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
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
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
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
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
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
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
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