I have the problem mostly solved, I just can't get the last line to be formatted correctly with spaces. I am close, but I need another set of eyes on this
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
const int MAX_CHAR = 16;
int main(int argc, char *argv[])
{
//command line argument for filename
char *fileName = argv[1];
//open file and check for validity
FILE *file = fopen(fileName, "r");
if (file == NULL)
{
printf("The file was not opened correctly\n");
return -1;
}
char line[MAX_CHAR]; //array to store characters
int lineCount = 0; //counter for hexadecimal line display
int charCount = 0; //counter for spacing of hex ascii values
int breakIndex = MAX_CHAR; //integer to save what index the array is stopped at EOF
//cycle through every 16 characters
while(!feof(file))
{
//read 16 characters
for(int i = 0; i<breakIndex ; i++)
{
line[i]=fgetc(file);
if (feof(file))
{
breakIndex = i;
line[i]='\0';
break;
}
}
//print line count in hexadecimal
printf("%.08x: ",lineCount);
//print hexadecimal for each character
for(int i = 0; i<breakIndex; i++)
{
printf("%.02x",line[i]);
charCount++;
if(charCount % 2 == 0)
{
printf(" ");
}
}
//spaces for formatting output
if(breakIndex == MAX_CHAR)
{
printf(" ");
}
else
{
int spaces = (MAX_CHAR - breakIndex) * 2 ;
//printf("----%d-----",spaces);
for(int i = 0 ; i< spaces ; i++)
{
printf(" ");
}
}
//print actual characters
for(int i = 0; i<breakIndex ; i++)
{
if(!isprint(line[i]))
{
line[i]= '.';
}
}
line[16] = '\0';
printf("%s",line);
printf("\n");
//increment line count
lineCount+=16;
}
//close file
fclose(file);
return 0;
}
My problem lies in the "spaces for formatting output section of code. I have accounted for the hexadecimal digits but I can't formulate how to account for the other spaces needed.
this is my output this is the actual xxd output I need this to work for any length file given
To largely summarize the comments:
lineis an array (variable-length) of length 16, with the valid indicies [0, 15].line[16] = '\0'indexes a seventeenth element, that does not exist. This is out-of-bounds array access, which invokes Undefined Behaviour.while(!feof(file))is always wrong - avoid it. Test the results of your I/O functions directly (important: useinttype to hold the value returned byfgetc, if you want to reliably test forEOF. See below.).If
argv[1]isNULL(i.e.,argc < 2), this also invokes UB. (Additionally,xxdmimicry would involve reading fromstdinif no input file is specified, but we can put that aside for now.)Generally speaking, errors should be written to
stderr.If you would like to avoid null-terminating your buffer, you can print up to a fixed amount of characters from a buffer with
printf("%.*s", (int) n, buffer).One important thing to note is that the
%xspecifier expects anunsigned intargument, andprintfarguments are subject to integer promotions.charis a unique type, distinct fromsigned charorunsigned char, and its signed-ness is implementation defined. This uniqueness is the same reason you cannot usecharto reliably test for the negative value ofEOF(as in the return value fromfgetc), as the value may become indistinguishable from another valid value in the event thatcharis unsigned.Use
unsigned charfor your buffer (and%hhxfor your specifier, if you want to be super-correct about it).(Without this, you may get large hexadecimal values from signed bytes in your file. Think binary.)
lineCountshould also beunsigned int(or usesize_t, and%zx).As for spacing, the total amount of characters to write per line for the hexadecimal pairs block is
(Assuming integer division.)
Broken down, the format is:
That said, a simple way to do this is to is to always iterate
MAX_CHARtimes, and decide if you are printing a 2-character hex octet, or two spaces when you run out of valid octets. A delimiting space is printed on every odd-iteration or always on the final iteration.Following this, a modified version of your example looks like:
Alternatively, this can be done with
fread, reading chunks of data: