Wednesday 30 April 2008

C#: file hex dump application

A simple application for printing file contents as hexadecimal.

Usage

For multiple bytes per line:

C:\>   PrintHex.exe -h PrintHex.cs


Sample output:
75 73 69 6E 67 20 53 79 73 74 65 6D 3B 0D 0A 75   using System;__u
73 69 6E 67 20 53 79 73 74 65 6D 2E 49 4F 3B 0D   sing System.IO;_
0A 0D 0A 2F 2A 2A 0D 0A 2A 20 41 70 70 6C 69 63   ___/**__* Applic
61 74 69 6F 6E 20 66 6F 72 20 70 72 69 6E 74 69   ation for printi
6E 67 20 66 69 6C 65 20 74 6F 20 63 6F 6E 73 6F   ng file to conso
6C 65 20 61 73 20 68 65 78 61 64 65 63 69 6D 61   le as hexadecima
6C 2E 0D 0A 2A 2F 0D 0A 70 75 62 6C 69 63 20 63   l.__*/__public c


For single byte per line:

C:\>   PrintHex.exe -s PrintHex.cs


Sample output:
u       117     75
s       115     73
i       105     69
n       110     6E
g       103     67
        32      20
S       83      53
y       121     79
s       115     73
t       116     74
e       101     65
m       109     6D
;       59      3B


Listing: PrintHex.cs

using System;
using System.IO;

/**
* Application for printing file to console as hexadecimal.
*/
public class PrintHex {
    
    public static void Main(String[] args) {
        if(args.Length!=2) {
            PrintUsage();
            return;
        }
        
        IEmitter emitter;
        if(args[0] == "-h") {
            emitter = new HexEmitter();
        } else if(args[0] == "-s") {
            emitter = new PerLineEmitter();
        } else {
            PrintUsage();
            return;
        }
        
        try {
            
            string path = args[1];
            
            if(!File.Exists(path)) {
                Console.WriteLine("File does not exist: {0}", path);
                return;
            }
            
            using(FileStream fs = File.OpenRead(path)) {
                while(true) {
                    int b = fs.ReadByte();
                    if(b == -1) {
                        break;
                    }
                    emitter.PrintByte(b);
                }
                emitter.Flush();
            }
            
        } catch(Exception e) {
            Console.WriteLine(e);
        }
    }
    
    public static void PrintUsage() {
        Console.WriteLine("usage: PrintHex.exe -<switch> <filename>");
        Console.WriteLine("  -h  hex dump");
        Console.WriteLine("  -s  one byte per line");
    }
    
}

/**
* Interface for formatting byte output.
*/
public interface IEmitter {
    /**Called for every byte*/
    void PrintByte(int b);
    /**Called after class is finished with*/
    void Flush();
}

/**
* Prints one byte per line as character, decimal and hex value.
*/
public class PerLineEmitter : IEmitter {
    public void PrintByte(int b) {
        char ch = Util.ToSafeAscii(b);
        Console.WriteLine(ch+"\t"+b+"\t"+Util.ToHexString(b));
    }
    public void Flush() {}
}

/**
* Prints multiple bytes per line followed by characters.
*/
public class HexEmitter : IEmitter {
    private const int NBUFF = 16;
    private int buffered = 0;
    private int[] bytes = new int[NBUFF];
    public void PrintByte(int b) {
        bytes[buffered] = b;
        buffered++;
        if(buffered == NBUFF) {
            Flush();
        }
    }
    public void Flush() {
        if(buffered<=0) {
            return;
        }
        
        for(int i=0; i<NBUFF; i++) {
            if(i >= buffered) {
                Console.Write("   ");
            } else {
                string hex = Util.ToHexString(bytes[i]);
                Console.Write(hex);
                Console.Write(" ");
            }
        }
        
        Console.Write("  ");
        
        for(int i=0; i<NBUFF; i++) {
            if(i >= buffered) {
                Console.Write(" ");
            } else {
                char ch = Util.ToSafeAscii(bytes[i]);
                Console.Write(ch);
            }
        }
        
        Console.WriteLine();
        
        buffered = 0;
    }
}

/**
* Utility methods.
*/
public class Util {
    
    /**
    * Converts a byte to a hexadecimal string value.
    */
    public static string ToHexString(int b) {
        const int mask1 = 0x0F;
        const int mask2 = 0xF0;
        
        string ret = "";
        
        int c1 = (b & mask1);
        int c2 = (b & mask2) >> 4;
        
        ret = ret + ToHexChar(c2) + ToHexChar(c1);
        return ret;
    }
    
    /**
    * Converts the given byte to a hex character.
    */
    private static char ToHexChar(int b) {
        const int ascii_zero = 48;
        const int ascii_a = 65;
        
        if(b>=0 && b<=9) {
            return (char) (b + ascii_zero);
        }
        if(b>=10 && b<=15) {
            return (char) (b + ascii_a - 10);
        }
        return '?';
    }
    
    /**
    * Converts the byte to a visible ASCII character or
    * underscore if it is not.
    */
    public static char ToSafeAscii(int b) {
        if(b>=32 && b<=126) {
            return (char) b;
        }
        return '_';
    }
    
}

2 comments:

  1. Thanks, I was having the hardest time finding something like this.

    ReplyDelete
  2. Easy typo to fix, had to use lowercase 's' in void Main(String[] args) to avoid the "Does not contain a static 'Main' method suitable for an entry point" compiler error for me, at least when using the command-line csc compiler. Works like a charm and is a life saver! At a client site where they don't have internet access, no clipboard copy+paste and transferring files/installers is a red-taped process so it was easiest for me to re-type this by hand and compile it there. Thanks!

    ReplyDelete

All comments are moderated