Przegląd pliku w postaci szesnastkowej

Program wyświetla zawartość pliku w trybie szesnastkowo-tekstowym. Prezentuje obsługę parametrów wywołania programu, operacje na plikach w trybie binarnym oraz wykorzystanie funkcji formatowanego wyjścia printf.

Program pobiera nazwę pliku z linii poleceń, jeżeli nie ma nazwy w linii poleceń, program pyta o tę nazwę użytkownika. Do przetwarzania pliku wykorzystano odczyt blokowy. Program stanowi uzupełnienie wykładu z programowania w językach C/C++. Koncepcja przetwarzania plików przedstawiona została tutaj.

Do pobrania wersja źródłowa pliku (hex_view.c).

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

/* Liczba linii na jednej stronie - ekranie */
#define PAGE_LENGTH 24

/* Liczba znaków odczytywanych do bufora - tyle znaków będzie w pojedynczej
   linii na stronie, najpierw w postaci HEX, potem ASCII */
#define BUFFER_LEN  19

char * read_string(char * buffer, size_t buffer_len)
{
  // Funkcja fgets pozwala zabezpieczyć się przed
  // przepełnieniem bufora, stosujemy zamiast funkcji gets.
  // Funkcja ta jednak pozostawia znak '\n' na końcu linii,
  // trzeba go usunąć. Można posłużyć się długością łańcucha
  // i manipulować indeksami, ja wolę poszukać czy nadmiarowy
  // znak występuje i przyciąć łańcuch na jego pozycji
  char * new_line_char;
  fgets(buffer, buffer_len - 2, stdin);
  if((new_line_char = strchr(buffer, '\n')) != 0)
    *new_line_char = '\0';
  return buffer;
}

int main(int argc, char *args[] )
{
   char   file_name[ 256 ];            /* Bufor na nazwę pliku */
   FILE * file;                        /* Wskaźnik plikowy */
   int in_chars;                       /* Liczba znaków odczytanych z pliku
                                          przy pojedynczym odczycie */
   unsigned char buffer[ BUFFER_LEN ]; /* Bufor na odczytywane znaki */
   int lines = 0;                      /* Licznik linii "zapełnianych" na
                                          ekranie */
   int i = 0;                          /* Zmienna sterująca iteracji */

   if(argc == 1) /* Czy nazwa pliku występuje w linii polecen? */
   {
     printf("Enter a filename: "); /* Zapytaj o nazwę pliku */
     read_string(file_name, 256);   /* Wczytaj nazwę pliku */
   }
   else
     strcpy(file_name, args[ 1 ]);  /* Pobierz nazwę pliku z tablicy parametrów */
   if((file = fopen( file_name, "rb")) == NULL)
   {
      printf("Sorry, can't open \"%s\" press Enter...", file_name);
      (void)getchar(); /* Czekanie na Enter */
      return EXIT_FAILURE;
   }

   /* Odczytuj kolejne bloki, zapamiętaj w zmiennej in_chars ile wczytano
      znaków za każdym razem. Jeżeli wczytano < 0 to wystąpił koniec pliku */
   while((in_chars = fread(buffer, 1, BUFFER_LEN, file)) > 0)
   {
      /* Wyświetl zawartość bufora szesnastkowo, na dwóch pozycjach,
      z wiodącym zerem, dużymi literami - specyfikacja %02X */
      for(i = 0; i < BUFFER_LEN; i++)
         if(i < in_chars)
           printf("%02X ", buffer[i]);
         else /* Gdy w buforze jest mniej znaków, zwykle ostatni odczyt z pliku */
           printf("   "); 

      printf("| ");  /* Separator części szesnastkowej od ASCII */
      /* Wyświetla bufor jako ASCII o ile można, jeżeli nie,
         to wyswietla '.' */
      for(i = 0; i < in_chars; i++)
         printf("%c", isprint(buffer[ i ]) ? buffer[i] : '.');
      putchar('\n');
      if((++lines % PAGE_LENGTH) == 0)  /* Czy ekran zapełniony? */
      {
        printf( "---- Press Enter for more ---" );
        (void)getchar(); /* Czekanie na Enter */
      }
   }
   fclose(file); /* Zamykamy plik */
   return EXIT_SUCCESS;
}