Input Format Specifiers

From Nutwiki
Jump to: navigation, search

Test Environments

Hardware Comments Nut/OS
4.8.7
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3

Overview

In this example you will learn how to use the different input format specifiers to obtain specific datatypes via the scanf and fscanf functions. These are useful if your program needs to get a certain value from the user or has to read a file of which you know the structure.

Source Code

<source lang="c">

  1. include <dev/board.h>
  2. include <stdio.h>
  3. include <io.h>

int main(void) {

   unsigned long baud = 115200;
   char uname[25];
   char fletter = 0;
   unsigned int age = 0;
   int agediff = 0;
   int oct = 0;
   int hexa = 0;
   NutRegisterDevice(&DEV_UART, 0, 0);
   freopen(DEV_UART_NAME, "w", stdout);
   freopen(DEV_UART_NAME, "r", stdin);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS Input Format Specifiers Demo");
   puts("\nYou will now be prompted for a series of inputs to showcase the Nut/OS input format specifiers.");
   puts("Question 1: What is your name? (String)");
   scanf("%s", uname);
   puts("Question 2: What is your favorite letter? (Character)");
   scanf("%c", &fletter);
   puts("Question 3: How old are you? (Unsigned Integer)");
   scanf("%u", &age);
   puts("Question 4: Let's be honest, by how many years does your real age differ from the one you just entered? (Signed Integer)");
   scanf("%i", &agediff);
   puts("Question 5: BONUS ROUND! Why do programmers often mix up halloween and christmas?\nBecause 25 DEC = ... OCT (Octal)");
   scanf("%o", &oct);
   if (oct == 25) {
       puts("Exactly!");
   } else {
       puts("Nope, sorry.");
   }
   puts("Final question: Which value are we looking for?\n\nRoses are #FF0000,\nViolets are #...,\nHexadecimals are sweet;\nDon't you think so, too? (HEX)");
   fscanf(stdin, "%x", &hexa);
   if (hexa == 0x0000ff) {
       puts("Exactly!");
   } else {
       puts("Nope, sorry.");
   }
   for (;;) {
   }
   return 0;

} </source>

Details

<source lang="c"> int main(void) {

   unsigned long baud = 115200;
   char uname[25];
   char fletter = 0;
   unsigned int age = 0;
   int agediff = 0;
   int oct = 0;
   int hexa = 0;
   NutRegisterDevice(&DEV_UART, 0, 0);
   freopen(DEV_UART_NAME, "w", stdout);
   freopen(DEV_UART_NAME, "r", stdin);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);

</source>

Let's begin. We declare a number of variables that will hold the value of the data the user will enter. These are nothing fancy, they are just all different types so we can easily how we can read in a certain datatype.

After that we register the UART device and reassign stdin and stdout to it. This allows us to use the UART device for both reading and writing while the DEV_DEBUG driver only provides write access to the UART. There are some more differences between the two but nothing we have to care about right now.

<source lang="c">

   puts("\nNut/OS Input Format Specifiers Demo");
   puts("\nYou will now be prompted for a series of inputs to showcase the Nut/OS input format specifiers.");
   puts("Question 1: What is your name? (String)");
   scanf("%s", uname);

</source>

Now we print an application name as always, explain what this demo is about and get going with the first question.

In case you never used scanf, here is a short explanation: scanf reads an amount of data from stdin. How this is interpreted and how much it reads depends on the input format specifiers used.

The input format specifiers are passed as a string, in this case "%s" (which means "string"). It is possible to pass multiple specifiers as long as you pass the same number of variables with correct datatypes to store it into.

The second parameter to scanf has to be a pointer to the place you want to store the input. Since the char array ("string") uname is a pointer already, we just pass it by simply entering the variable name here.

Make sure you pass the exact same number and type of arguments here as you use in the format specifier string!

Moving on.

<source lang="c">

   puts("Question 2: What is your favorite letter? (Character)");
   scanf("%c", &fletter);

</source>

This time we enter a character ("%c") instead of a string.

The second entry is pretty similar except for one important difference: The first entry required a char pointer to be passed since we entered multiple characters. This time we only enter a single character. Because of this, we are using the char datatype which is NOT a pointer to something. If we just passed the variable name here, the contents of variable fletter would get copied and passed to the function, overwritten by the user input and then dropped. The original variable would not be touched and keep its original value. Since this is not what we want to do, we pass a pointer to fletter by prepending it with the & character. Now it works!

<source lang="c">

   puts("Question 3: How old are you? (Unsigned Integer)");
   scanf("%u", &age);
   puts("Question 4: Let's be honest, by how many years does your real age differ from the one you just entered? (Signed Integer)");
   scanf("%i", &agediff);
   puts("Question 5: BONUS ROUND! Why do programmers often mix up halloween and christmas?\nBecause 25 DEC = ... OCT (Octal)");
   scanf("%o", &oct);
   if (oct == 25) {
       puts("Exactly!");
   } else {
       puts("Nope, sorry.");
   }

</source>

Since the inputs here don't differ much from the ones above, we'll only take a look at the different format specifiers used:

%u means "unsigned integer". This allows the user to enter an integer above or equal to 0 but not below it.

%i means "signed integer". It stores the same numbers as the normal integer with the addition of negative numbers. This also allows the user to prepend a number with a + or - sign to decide whether it is positive or negative.

%o is basically the same as the integer with the difference that it expects input to be in base 8, the octal system.

The number itself is unaffected by this. As you can see, we compare the entered octal number to the decimal 25 after it is entered. Since the user can only enter octal here, the statement will only be true if the user entered 31.

<source lang="c">

   puts("Final question: Which value are we looking for?\n\nRoses are #FF0000,\nViolets are #...,\nHexadecimals are sweet;\nDon't you think so, too? (HEX)");
   fscanf(stdin, "%x", &hexa);
   if (hexa == 0x0000ff) {
       puts("Exactly!");
   } else {
       puts("Nope, sorry.");
   }

</source>

The final question contains the final format specifier and a new function. Not that new, actually, as you will see in a moment.

The "%x" stands for hexadecimal. You know, that system that colors in painting programs are always denoted in. And... well... lots of stuff in the programming world so you'll probably need this specifier sooner or later. Hexadecimal means base 16 so it ranges from 0-F (where the letters A-F are used as makeshift digits above the 9).

Now for that fscanf. What is it?

fscanf works in the same way as scanf but it does not read from stdin but from a file. That file must already be open for reading and you have to pass the filepointer as first argument but that's it for the differences between the two functions.

The way it is used here it is the same as scanf. That is because scanf always reads from stdin, which is a filepointer. scanf(format, variables) is really just short for fscanf(stdin, format, variables).

Output

<source lang="text">

Nut/OS Input Format Specifiers Demo

You will now be prompted for a series of inputs to showcase the Nut/OS input for mat specifiers. Question 1: What is your name? (String) Question 2: What is your favorite letter? (Character) Question 3: How old are you? (Unsigned Integer) Question 4: Let's be honest, by how many years does your real age differ from th e one you just entered? (Signed Integer) Question 5: BONUS ROUND! Why do programmers often mix up halloween and christmas ? Because 25 DEC = ... OCT (Octal) Exactly! Final question: Which value are we looking for?

Roses are #FF0000, Violets are #..., Hexadecimals are sweet; Don't you think so, too? (HEX) Exactly! </source>

Advanced functionality

There is also a function vfscanf. It is functionally identical to fscanf but takes a va_list instead of multiple arguments as parameter. Due to the similarity to fscanf an example for this function is not provided.

See also