Reading from the Standard Input
Any data that is read by the program must first be stored in a variable. For example, a program that reads the number of students from the input must store this information in a variable. The type of this specific variable can be int
.
As we've seen in the previous chapter, we don't need to type stdout
when printing to the output, because it is implied. Further, what is to be printed is specified as the argument. So, write(studentCount)
is sufficient to print the value of studentCount
. To summarize:
stream: stdout operation: write data: the value of the studentCount variable target: commonly the terminal window
The reverse of write
is readf
; it reads from the standard input. The "f" in its name comes from "formatted" as what it reads must always be presented in a certain format.
We've also seen in the previous chapter that the standard input stream is stdin
.
In the case of reading, one piece of the puzzle is still missing: where to store the data. To summarize:
stream: stdin operation: readf data: some information target: ?
The location of where to store the data is specified by the address of a variable. The address of a variable is the exact location in the computer's memory where its value is stored.
In D, the &
character that is typed before a name is the address of what that name represents. For example, the address of studentCount
is &studentCount
. Here, &studentCount
can be read as "the address of studentCount
" and is the missing piece to replace the question mark above:
stream: stdin operation: readf data: some information target: the location of the studentCount variable
Typing a &
in front of a name means pointing at what that name represents. This concept is the foundation of references and pointers that we will see in later chapters.
I will leave one peculiarity about the use of readf
for later; for now, let's accept as a rule that the first argument to readf
must be "%s"
:
readf("%s", &studentCount);
Actually, readf
can work without the &
character as well:
readf("%s", studentCount); // same as above
Although the code is cleaner and safer without the &
character, I will continue to use readf
with pointers partly to prepare you to the concepts of references and reference function parameters.
"%s"
indicates that the data should automatically be converted in a way that is suitable to the type of the variable. For example, when the '4' and '2' characters are read to a variable of type int
, they are converted to the integer value 42.
The program below asks the user to enter the number of students. You must press the Enter key after typing the input:
import std.stdio; void main() { write("How many students are there? "); /* The definition of the variable that will be used to * store the information that is read from the input. */ int studentCount; // Storing the input data to that variable readf("%s", &studentCount); writeln("Got it: There are ", studentCount, " students."); }
Skipping the whitespace characters
Even the Enter key that we press after typing the data is stored as a special code and is placed into the stdin
stream. This is useful to the programs to detect whether the information has been input on a single line or multiple lines.
Although sometimes useful, such special codes are mostly not important for the program and must be filtered out from the input. Otherwise they block the input and prevent reading other data.
To see this problem in a program, let's also read the number of teachers from the input:
import std.stdio; void main() { write("How many students are there? "); int studentCount; readf("%s", &studentCount); write("How many teachers are there? "); int teacherCount; readf("%s", &teacherCount); writeln("Got it: There are ", studentCount, " students", " and ", teacherCount, " teachers."); }
Unfortunately, the program cannot use that special code when expecting an int
value:
How many students are there? 100
← An exception is thrown here
The special code(s) that represents the Enter key that has been pressed when entering the previous 100 is still in the input stream and is blocking it:
100[EnterCode]
The solution is to use a space character before %s
to indicate that the Enter code that appears before reading the number of teachers is not important: " %s"
. Spaces that are in the format strings are used for reading and ignoring zero or more invisible characters that would otherwise appear in the input. Such characters include the actual space character, the code(s) that represent the Enter key, the Tab character, etc. and are called the whitespace characters.
As a general rule, you can use " %s"
for every data that is read from the input. The program above works as expected with the following changes:
// ... readf(" %s", &studentCount); // ... readf(" %s", &teacherCount); // ...
The output:
How many students are there? 100 How many teachers are there? 20 Got it: There are 100 students and 20 teachers.
Additional information
- Lines that start with // are useful for single lines of comments. To write multiple lines as a single comment, enclose the lines within /* and */ markers.
In order to be able to comment even other comments, use /+ and +/:
/+ // A single line of comment /* A comment that spans multiple lines */ /+ It can even include nested /+ comments +/ +/ A comment block that includes other comments +/
- Most of the whitespace in the source code is insignificant. It is good practice to write longer expressions as multiple lines or add extra whitespace to make the code more readable. Still, as long as the syntax rules of the language are observed, the programs can be written without any extra whitespace:
import std.stdio;void main(){writeln("Hard to read!");}
It can be hard to read source code with small amounts of whitespace.
Exercise
Enter non-numerical characters when the program is expecting integer values and observe that the program does not work correctly.