www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Calling readln() after readf

reply PinDPlugga <a a.com> writes:
I didn't want to necropost, but I ran into the same behaviour as 
in this post:
https://forum.dlang.org/post/yfybveovbknvvxmioivd forum.dlang.org
and was just curious to understand it better.

If I call readln() after having previously called readf(), it 
does not behave as expected:
```d
import std.stdio, std.string;

void main()
{
     write("Please enter a number: ");
     double number;
     readf(" %s", number);

     write("Please enter a string: ");
     string input = strip(readln());

     writefln!("number: %s --- string: %s")(number, input);
}
```
Gives me the following:
```
Please enter a number: 1
Please enter a string: number: 1 ---- string:
```
I know I can get this to work replacing the `strin(readln())` 
with `readf(" %s\n", input)`, but I also found if I just call 
`strip(readln())` an extra time this worked as well, but what is 
confusing to me is if I have multiple readf's I still only need 
to call readln one extra time to get it to work as expected:
```d
import std.stdio, std.string;

void main()
{
     write("Please enter a number: ");
     double number1;
     readf(" %s", number1);
     write("Please enter a number: ");
     double number2;
     readf(" %s", number2);

     // Handle what should be two \n's from readf?
     string input = strip(readln());

     // Continue as normal
     write("Please enter a string: ");
     input = strip(readln());

     writefln!("number1: %s --- number2: %s --- string: %s")
         (number1, number2, input);
}
```
And this works.
```
Please enter a number: 1
Please enter a number: 2
Please enter a string: hello
number1: 1 --- number2: 2 --- string: hello
```
Could anyone help explain this to me, and also is there a better 
way to handle this when wanting to use a mix of readf and readln?
Apr 24 2021
next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Saturday, 24 April 2021 at 14:46:06 UTC, PinDPlugga wrote:
 I didn't want to necropost, but I ran into the same behaviour 
 as in this post:
 https://forum.dlang.org/post/yfybveovbknvvxmioivd forum.dlang.org
 and was just curious to understand it better.

 [...]
https://dlang.org/library/std/string/chomp.html
Apr 24 2021
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 4/24/21 7:46 AM, PinDPlugga wrote:

      write("Please enter a number: ");
      double number;
      readf(" %s", number);
Just to make sure, this is a common issue for other languages as well. As the explanation, the character ('\n') that you injected into stdin by pressing Enter is not (and should not be) removed by readf.
      write("Please enter a string: ");
      string input = strip(readln());
Because of that '\n' character, that readln() reads an empty line. There is no way other than reading and discarding all input at stdin but luckily, readf's "formatted" behavior is able to take care of it because any character that match the format string will be read and discarded by readf: readf(" %s\n", number); As a side note, that '\n' should work on all platforms even if the terminal injects two characters. As a general solution, you can use a function like this: auto readLine(S = string)(File file = stdin) { while (!file.eof) { auto line = file.readln!S.strip; if (!line.empty) { return line; } } return null; } Ali
Apr 24 2021
parent reply Gary Chike <chikega gmail.com> writes:
On Saturday, 24 April 2021 at 22:13:45 UTC, Ali Çehreli wrote:
 On 4/24/21 7:46 AM, PinDPlugga wrote:
 ...
 As a general solution, you can use a function like this:

 auto readLine(S = string)(File file = stdin) {
   while (!file.eof) {
     auto line = file.readln!S.strip;
     if (!line.empty) {
       return line;
     }
   }

   return null;
 }

 Ali
Hi Ali, Being a new D learner, I've noticed this behavior as well. Thank you for providing the 'readLine' function! Would it be considered poor coding style to intercept the stream between readf and readln with the following statement? : ```d auto catcher = readln.strip; ``` P.S. I love your book on D! :) Cheers, Gary Chike
Jun 19 2022
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 6/19/22 15:52, Gary Chike wrote:
 On Saturday, 24 April 2021 at 22:13:45 UTC, Ali Çehreli wrote:
 On 4/24/21 7:46 AM, PinDPlugga wrote:
 ...
 As a general solution, you can use a function like this:

 auto readLine(S = string)(File file = stdin) {
   while (!file.eof) {
     auto line = file.readln!S.strip;
     if (!line.empty) {
       return line;
     }
   }

   return null;
 }

 Ali
Hi Ali, Being a new D learner, I've noticed this behavior as well. Thank you for providing the 'readLine' function! Would it be considered poor coding style to intercept the stream between readf and readln with the following statement? : ```d auto catcher = readln.strip; ```
The original program said "Please enter a number: ". If the program was interacting with a human and the human entered a number, then the rest of the line would be understood to be ignored and your method works. On the other hand, the readLine() function would not ignore the rest of the line and use it. But I think yours makes more sense. :) But when the program interacts with piped data, there may be multiple \n characters and all of those might have to be ignored before reading the next non-empty line. So you may want to do readln.strip multiple times? I don't know. It all depends on the use case.
 P.S. I love your book on D! :)
Thank you! :)
 Cheers,

 Gary Chike
Ali
Jun 19 2022
parent reply Gary Chike <chikega gmail.com> writes:
On Monday, 20 June 2022 at 00:43:17 UTC, Ali Çehreli wrote:
 ...
 But when the program interacts with piped data, there may be 
 multiple \n characters and all of those might have to be 
 ignored before reading the next non-empty line. So you may want 
 to do readln.strip multiple times? I don't know. It all depends 
 on the use case.

 Ali
Thank you for your input, Ali! Would it be appropriate to forego `readf` and read input as a string using `readln` ,benefiting from the `strip` function, then convert to their appropriate datatype as in the following example?: ```d module hello_world; import std.stdio; import std.string; // strip() import std.conv; // to!int(),to!float() void main() { writeln("Hello world from D!"); write("What is your name? "); auto fname = readln.strip; writeln("It's so nice to meet you ", fname, "!"); write("How old are you? "); auto age = to!int(readln.strip); write("What is your GPA? "); auto gpa = to!float(readln.strip); writeln("So, ", fname, ", you are ", age, " years old and your GPA is ", gpa, ".. stellar!"); } ``` Also I signed up with Educative.io to do your interactive D language lessons. It really goes so well with your book :) Cheers, Gary Chike
Jun 20 2022
next sibling parent Gary Chike <chikega gmail.com> writes:
I should have qualified the above with:  _ and I will be 
performing operations on the numeric datatype, since just 
outputting simply strings, as in the above example, would require 
nothing more than string data types. :)
```d
module hello_world;

import std.stdio;
import std.string; // strip()
import std.conv; // to!int(),to!float()

void main() {
	writeln("Hello world from D!");
	write("What is your name? ");
	auto fname = readln.strip;
	writeln("It's so nice to meet you ", fname, "!");
	write("How old are you? ");
	auto age = to!int(readln.strip);
	write("What is your GPA? ");
	auto gpa = to!float(readln.strip);
	if (gpa > 4) {
		writeln("Sorry, 4 is the highest GPA ");
		gpa = 4; }
	writeln("So, ", fname, ", you are ", age, " years old and your 
GPA is ",
		gpa, ".. stellar!");
	writeln("Can you increase your GPA? ");
	if(gpa == 4) {
		writeln("Your GPA is already as high as it can be!");
	} else {		
		gpa = gpa > 3.5 ? 4 : (gpa + 0.5);
		writeln("If you study hard, your GPA will likely be ", gpa);
		age = age + 4;
		writeln("But that may take 4 years, and you will be ", age, " 
years old by that time.");
	}
}
```
Jun 20 2022
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 6/20/22 07:00, Gary Chike wrote:

 Would it be appropriate to forego `readf`
 and read input as a string using `readln` ,benefiting from the `strip`
 function, then convert to their appropriate datatype
Makes sense. The following are related as well: https://dlang.org/library/std/conv/parse.html https://dlang.org/library/std/format/read/formatted_read.html Ali
Jun 20 2022
next sibling parent Gary Chike <chikega gmail.com> writes:
On Monday, 20 June 2022 at 16:08:33 UTC, Ali Çehreli wrote:
 Makes sense. The following are related as well:

   https://dlang.org/library/std/conv/parse.html

   https://dlang.org/library/std/format/read/formatted_read.html

 Ali
Thank you so much for your input! Cheers! :)
Jun 26 2022
prev sibling parent reply Gary Chike <chikega gmail.com> writes:
On Monday, 20 June 2022 at 16:08:33 UTC, Ali Çehreli wrote:
 On 6/20/22 07:00, Gary Chike wrote:

 Would it be appropriate to forego `readf`
 and read input as a string using `readln` ,benefiting from
the `strip`
 function, then convert to their appropriate datatype
Makes sense. The following are related as well: https://dlang.org/library/std/conv/parse.html https://dlang.org/library/std/format/read/formatted_read.html Ali
It's interesting from my own experimentation and observation, comparing `parse!int()` vs `to!int()` For instance, I can formulate a statement such as this using `to!int()`: `auto age = to!int(readln.strip());` but the same syntax using `parse!int()` would generate an error: `auto age = parse!int(readln.strip());` So, in order to use `parse!int()`, I would need to separate it into two statements with a variable acting as an intermediary: ``` auto input = readln(); auto age = parse!int(input);
Jul 05 2022
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 7/5/22 17:14, Gary Chike wrote:

 So, in order to use `parse!int()`, I would need to separate it into two
 statements with a variable acting as an intermediary:
 ```
 auto input = readln();
 auto age = parse!int(input);
Exactly. parse takes the input by reference (necessitating an lvalue) so that the input is consumed for the next step(s) of parsing. Ali
Jul 05 2022
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/5/22 8:14 PM, Gary Chike wrote:
 On Monday, 20 June 2022 at 16:08:33 UTC, Ali Çehreli wrote:
 On 6/20/22 07:00, Gary Chike wrote:

 Would it be appropriate to forego `readf`
 and read input as a string using `readln` ,benefiting from
the `strip`
 function, then convert to their appropriate datatype
Makes sense. The following are related as well:   https://dlang.org/library/std/conv/parse.html   https://dlang.org/library/std/format/read/formatted_read.html
It's interesting from my own experimentation and observation, comparing `parse!int()` vs `to!int()` For instance, I can formulate a statement such as this using `to!int()`: `auto age = to!int(readln.strip());` but the same syntax  using `parse!int()` would generate an error: `auto age = parse!int(readln.strip());` So, in order to use `parse!int()`, I would need to separate it into two statements with a variable acting as an intermediary: ``` auto input = readln(); auto age = parse!int(input);
Yes, you have 2 orthogonal desires: 1. I want to update the original range or not 2. I want to treat the entire range as representing my conversion type or just the first part. Phobos only handles 2 of the 4 possible scenarios. Well, really one scenario (reading the entire range, and wanting to update the original) is not important. -Steve
Jul 05 2022
parent Gary Chike <chikega gmail.com> writes:
Thanks for the extra info guys! D is one of my favorite languages 
I'm currently learning. :)
Jul 06 2022