CrownedEagle
CrownedEagle

Reputation: 125

What does the format specifiers "%02x " and "%3o " do in this code?

While searching for the meaning of the statement of K&R C Exercise 7-2 I foun this answer to the K&R C Exercise 7.2 on https://clc-wiki.net/wiki/K%26R2_solutions:Chapter_7:Exercise_2

The Exercise asks to

Write a program that will print arbitrary input in a sensible way. As a minimum, it should print non-graphic characters in octal or hexadecimal according to local custom, and break long text lines.

Also I am unable to understand the meaning of sentence of the exercise, specifically the part "As a minimum, it should print non-graphic characters in octal or hexadecimal according to local custom".

What does this exercise 7-2 asks for? Please explain the meaning of the statement of exercise.

The code below uses format specifiers "%02x " and "%3o " are used at the end of the code to print non-printable characters but what exactly this format specifiers do to Non-Printables?

 else
    {
      if(textrun > 0 || binaryrun + width >= split)
      {
        printf("\nBinary stream: ");
        textrun = 0;
        binaryrun = 15;
      }
      printf(format, ch);
      binaryrun += width;
    }

Rest of the code break long lines into smaller ones and print all printable characters as it is.

The Complete Program is as below:

    #include <stdio.h>

    #define OCTAL        8
    #define HEXADECIMAL 16


    void ProcessArgs(int argc, char *argv[], int *output)
    {
      int i = 0;
      while(argc > 1)
      {
        --argc;
        if(argv[argc][0] == '-')
        {
          i = 1;
          while(argv[argc][i] != '\0')
          {
            if(argv[argc][i] == 'o')
            {
              *output = OCTAL;
            }
            else if(argv[argc][i] == 'x')
            {
              *output = HEXADECIMAL;
            }
            else
            {
              /* Quietly ignore unknown switches, because we don't want to
              * interfere with the program's output. Later on in the
              * chapter, the delights of fprintf(stderr, "yadayadayada\n")
              * are revealed, just too late for this exercise.
              */
            }
          ++i;
          }
        }
      }
    }

    int can_print(int ch)
    {
      char *printable = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 !\"#%&'()*+,-./:;<=>?[\\]^_{|}~\t\f\v\r\n";

      char *s;
      int found = 0;

      for(s = printable; !found && *s; s++)
      {
        if(*s == ch)
        {
          found = 1;
        }
      }

      return found;
    }

    int main(int argc, char *argv[])
    {
      int split = 80;
      int output = HEXADECIMAL;
      int ch;
      int textrun = 0;
      int binaryrun = 0;
      char *format;
      int width = 0;

      ProcessArgs(argc, argv, &output);

      if(output == HEXADECIMAL)
      {
        format = "%02X ";
        width = 4;
      }
      else
      {
        format = "%3o ";
        width = 4;
      }

      while((ch = getchar()) != EOF)
      {
        if(can_print(ch))
        {
          if(binaryrun > 0)
          {
            putchar('\n');
            binaryrun = 0;
            textrun = 0;
          }
          putchar(ch);
          ++textrun;
          if(ch == '\n')
          {
            textrun = 0;
          }

          if(textrun == split)
          {
            putchar('\n');
            textrun = 0;
          }
        }
        else
        {
          if(textrun > 0 || binaryrun + width >= split)
          {
            printf("\nBinary stream: ");
            textrun = 0;
            binaryrun = 15;
          }
          printf(format, ch);
          binaryrun += width;
        }
      }

      putchar('\n');

      return 0;
    }

Upvotes: 0

Views: 1586

Answers (1)

the busybee
the busybee

Reputation: 12653

You have found by yourself what "%02x " and "%03o " mean. That is good!

So your question boils down to "What are non-printable characters?" and "How are they printed with the mentioned formats?"


A non-printable character is defined (in the source shown) by the string printable in function can_print(). All characters not in this string are deliberately defined to be non-printable. We can reason about the selection, but this is out of scope here. Another note: " " and "\t\f\v\r\n" are in this set of printable characters and have a value of <= 0x20 in ASCII.

BTW, the standard library has isprint() that checks for printability.


As you seem to know each character is encoded as an assigned value. This value can be interpreted as you like, as a character, as a number, as an instruction, as a colour, as a bit pattern, anything. Actually all digital computers are just working on bit patterns, it's up to the interpretation what they mean.

So a non-printable character can be interpreted as an int number, and this is what happens by printf() with the mentioned format. Let's say that the character read is '\b', known as backspace. (Note: It is not in printable.) In ASCII this character is encoded as 0x08. So the output will be "08 " and "010 ", respectively.

You might like to change the program in such way that all characters are considered non-printable. Then you'll see all characters output as hex or octal.

Upvotes: 4

Related Questions