C++ Coding Exercise – xxd – make hex dump on Windows


xxd is a Linux command utility that dumps the file content in hexadecimal. If you type in xxd -h, then you will have the following help menu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
~$ xxd --help
Usage:
       xxd [options] [infile [outfile]]
    or
       xxd -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]
Options:
    -a          toggle autoskip: A single '*' replaces nul-lines. Default off.
    -b          binary digit dump (incompatible with -ps,-i,-r). Default hex.
    -c cols     format cols octets per line. Default 16 (-i: 12, -ps: 30).
    -E          show characters in EBCDIC. Default ASCII.
    -g          number of octets per group in normal output. Default 2.
    -h          print this summary.
    -i          output in C include file style.
    -l len      stop after len octets.
    -ps         output in postscript plain hexdump style.
    -r          reverse operation: convert (or patch) hexdump into binary.
    -r -s off   revert with off added to file positions found in hexdump.
    -s [+][-]seek  start at seek bytes abs. (or +: rel.) infile offset.
    -u          use upper case hex letters.
~$ xxd --help
Usage:
       xxd [options] [infile [outfile]]
    or
       xxd -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]
Options:
    -a          toggle autoskip: A single '*' replaces nul-lines. Default off.
    -b          binary digit dump (incompatible with -ps,-i,-r). Default hex.
    -c cols     format cols octets per line. Default 16 (-i: 12, -ps: 30).
    -E          show characters in EBCDIC. Default ASCII.
    -g          number of octets per group in normal output. Default 2.
    -h          print this summary.
    -i          output in C include file style.
    -l len      stop after len octets.
    -ps         output in postscript plain hexdump style.
    -r          reverse operation: convert (or patch) hexdump into binary.
    -r -s off   revert with off added to file positions found in hexdump.
    -s [+][-]seek  start at seek bytes abs. (or +: rel.) infile offset.
    -u          use upper case hex letters.

Suppose you have a file like this, and xxd it will give the following:

1
2
3
4
~$ echo https://helloacm.com > hello.txt
~$ xxd hello.txt
0000000: 6874 7470 733a 2f2f 6865 6c6c 6f61 636d  https://helloacm
0000010: 2e63 6f6d 0a                             .com.
~$ echo https://helloacm.com > hello.txt
~$ xxd hello.txt
0000000: 6874 7470 733a 2f2f 6865 6c6c 6f61 636d  https://helloacm
0000010: 2e63 6f6d 0a                             .com.

And we can feed xxd using std-in.

1
2
3
4
5
~$ xxd
hello
acm
.com
0000000: 6865 6c6c 6f0a 6163 6d0a 2e63 6f6d 0a    hello.acm..com.
~$ xxd
hello
acm
.com
0000000: 6865 6c6c 6f0a 6163 6d0a 2e63 6f6d 0a    hello.acm..com.

You finish the input by pressing CTRL and D to indicate a EOF character (End of File).

So this utility is very useful in command line if you want to peek the content in hexadecimal (basically any files including binary). Can we have something simple and yet similar on Windows? Yes, we can create a tool like this (for the basic functionality) using C++ and compile it using g++ or other C++ compilers for windows.

xxd-on-windows C++ Coding Exercise - xxd - make hex dump on Windows c / c++ tools / utilities

xxd-on-windows

–help

This is simple.

1
2
3
4
5
6
void help() {
    cout << "xxd:  make a hexdump" << endl;
    cout << "visit https://HelloACM.com" << endl;
    cout << "-u uppercase" << endl;
    cout << "-c column" << endl;
}
void help() {
    cout << "xxd:  make a hexdump" << endl;
    cout << "visit https://HelloACM.com" << endl;
    cout << "-u uppercase" << endl;
    cout << "-c column" << endl;
}

We only have -u and -c options which cover the very basic functionality. Of course, you could easily extend the function if you like.

Check File Exits

The following function claims to be the fastest:

1
2
3
4
inline bool file_exists(const string& name) {
    struct stat buffer;
    return (stat (name.c_str(), &buffer) == 0);
}
inline bool file_exists(const string& name) {
    struct stat buffer;
    return (stat (name.c_str(), &buffer) == 0);
}

Converting Decimal to Hex

This simple utility is the core function of the whole tool, and here it is:

1
2
3
4
5
6
7
8
9
10
11
12
13
string tohex(int x, int w = 7, bool upper = false) {
    stringstream stream;
    stream << std::hex << (x & 0xFF);
    string result(stream.str());
    if (upper) {
        std::transform(result.begin(), result.end(), result.begin(), ::toupper);
    }
    int j = w - result.length();
    while (j-- > 0) { // zero padding
        result = '0' + result;
    }
    return result;
}
string tohex(int x, int w = 7, bool upper = false) {
    stringstream stream;
    stream << std::hex << (x & 0xFF);
    string result(stream.str());
    if (upper) {
        std::transform(result.begin(), result.end(), result.begin(), ::toupper);
    }
    int j = w - result.length();
    while (j-- > 0) { // zero padding
        result = '0' + result;
    }
    return result;
}

parameter w is the width of the hex string, padding with ‘0’. The parameter upper will turn the hex uppercase.

Get File Content into C++ string

The following function reads the binary file into a string variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::string get_file_contents(const char *filename)
{
      std::ifstream in(filename, std::ios::in | std::ios::binary);
      if (in) {
            std::string contents;
            in.seekg(0, std::ios::end);
            contents.resize(in.tellg());
            in.seekg(0, std::ios::beg);
            in.read(&contents[0], contents.size());
            in.close();
            return(contents);
      }
      throw(errno);
}
std::string get_file_contents(const char *filename)
{
      std::ifstream in(filename, std::ios::in | std::ios::binary);
      if (in) {
            std::string contents;
            in.seekg(0, std::ios::end);
            contents.resize(in.tellg());
            in.seekg(0, std::ios::beg);
            in.read(&contents[0], contents.size());
            in.close();
            return(contents);
      }
      throw(errno);
}

and then, we can easily have something like this:

1
2
3
4
5
6
7
void xxd(string filename, int w = 16, bool upper = false) {
    if (!file_exists(filename)) {
        cout << "xxd: " << filename << ": No such file or directory" << endl;
        return;
    }
    print_xxd(get_file_contents(filename.c_str()), w, upper);
}
void xxd(string filename, int w = 16, bool upper = false) {
    if (!file_exists(filename)) {
        cout << "xxd: " << filename << ": No such file or directory" << endl;
        return;
    }
    print_xxd(get_file_contents(filename.c_str()), w, upper);
}

print_xxd

The function, print_xxd() as you can see above, will make a hex dump on given string variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void print_xxd(string content, int w = 16, bool upper = false) {
    int len = content.size();
    int num = 0;
    string curline = "";
    int width = 9 + (w / 2) * 5 + 1;
    if (w & 1 == 1) {
        width += 3;
    }
    int cw = 1;
    int kk = 1;
    for (int i = 0; i < len ; i ++) {
        if ((i % w) == 0) {
            cout << tohex(num) << ": ";
            cw = 9;
        }
        char t = content[i];
        cout << tohex((int)t, 2, upper);
        cw += 2;
        curline += t;
        if ((i & 1) == kk) {
            cout << " ";
            cw += 1;
        }
        if ((i % w) == (w - 1)) {
            num += w;
            cout << "  ";
            cw += 2;
            int k = i - w + 1;
            for (int j = 0; j < w; j ++) {
                t = content[k ++];
                if ((int)t <= 32) { // non-printable characters
                    t = '.';
                }
                cout << t;
                cw ++;
            }
            cout << "\n\r";
            curline = "";
            cw = 0;
            if (w & 1 == 1) {
                kk = kk == 1 ? 0 : 1;
            }
        }
    }
    // remaining characters;
    int j = width - cw + 1;
    while (j-- > 0) {
        cout << " ";
    }
    for (int i = 0; i < curline.size(); i ++) {
        char t = content[i ++];
        if ((int)t < 32) { // non-printable characters
            t = '.';
        }
        cout << t;
    }
}
void print_xxd(string content, int w = 16, bool upper = false) {
    int len = content.size();
    int num = 0;
    string curline = "";
    int width = 9 + (w / 2) * 5 + 1;
    if (w & 1 == 1) {
        width += 3;
    }
    int cw = 1;
    int kk = 1;
    for (int i = 0; i < len ; i ++) {
        if ((i % w) == 0) {
            cout << tohex(num) << ": ";
            cw = 9;
        }
        char t = content[i];
        cout << tohex((int)t, 2, upper);
        cw += 2;
        curline += t;
        if ((i & 1) == kk) {
            cout << " ";
            cw += 1;
        }
        if ((i % w) == (w - 1)) {
            num += w;
            cout << "  ";
            cw += 2;
            int k = i - w + 1;
            for (int j = 0; j < w; j ++) {
                t = content[k ++];
                if ((int)t <= 32) { // non-printable characters
                    t = '.';
                }
                cout << t;
                cw ++;
            }
            cout << "\n\r";
            curline = "";
            cw = 0;
            if (w & 1 == 1) {
                kk = kk == 1 ? 0 : 1;
            }
        }
    }
    // remaining characters;
    int j = width - cw + 1;
    while (j-- > 0) {
        cout << " ";
    }
    for (int i = 0; i < curline.size(); i ++) {
        char t = content[i ++];
        if ((int)t < 32) { // non-printable characters
            t = '.';
        }
        cout << t;
    }
}

The difficulty is to print the remainder (empty spaces) for the last line. For non-printable characters (ASCII smaller than 32 – white space), print using a single ‘.’. We use a string variable curline to hold the characters that should be printed in current line. The variable width specifies the number of characters leading up to the actual content-print at the end of each line.

Standard In – stdin

As you can see from the example, the tool xxd actually supports reading from the stdin if no file name are given. So, if we find a filename in the command line, we set the flag io to false.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
int main(int argc, char ** argv)
{
    bool upper = false;
    int w = 16;
    bool io = true;
    for (int i = 2; i <= argc; i ++) {
        string cur = argv[i - 1];
        if (cur == "-h") {
            help();
            return 0;
        }
        if (cur == "-u") {
            upper = true;
        }
        else if ((cur[0] == '-') && (cur.length() > 1)) {
            if (cur[1] == 'c') {
                if (cur.length() > 2) {
                    string num = cur.substr(2);
                    std::stringstream str(num);
                    int x;
                    str >> x;
                    if (str) {
                        if (x > 0) {
                            w = x;
                        }
                    }
                } else {
                    if (i != argc) {
                        string num = string(argv[i]);
                        std::stringstream str(num);
                        int x;
                        str >> x;
                        if (str) {
                            if (x > 0) {
                                w = x;
                            }
                        }
                        i ++;
                    }
                }
            }
        }
        else {
            xxd(cur, w, upper);
            io = false;
        }
    }
    if (io) { 
        // standard input
        std::string line;
        std::string all = "";
        while (std::getline(std::cin, line))
        {
            all += line;
        }
        print_xxd(all, w, upper);
    }
    return 0;
}
int main(int argc, char ** argv)
{
    bool upper = false;
    int w = 16;
    bool io = true;
    for (int i = 2; i <= argc; i ++) {
        string cur = argv[i - 1];
        if (cur == "-h") {
            help();
            return 0;
        }
        if (cur == "-u") {
            upper = true;
        }
        else if ((cur[0] == '-') && (cur.length() > 1)) {
            if (cur[1] == 'c') {
                if (cur.length() > 2) {
                    string num = cur.substr(2);
                    std::stringstream str(num);
                    int x;
                    str >> x;
                    if (str) {
                        if (x > 0) {
                            w = x;
                        }
                    }
                } else {
                    if (i != argc) {
                        string num = string(argv[i]);
                        std::stringstream str(num);
                        int x;
                        str >> x;
                        if (str) {
                            if (x > 0) {
                                w = x;
                            }
                        }
                        i ++;
                    }
                }
            }
        }
        else {
            xxd(cur, w, upper);
            io = false;
        }
    }
    if (io) { 
        // standard input
        std::string line;
        std::string all = "";
        while (std::getline(std::cin, line))
        {
            all += line;
        }
        print_xxd(all, w, upper);
    }
    return 0;
}

Windows Pre-compiled Binary

Using g++.

1
2
mingw32-g++ (tdm-1) 4.7.1
mingw32-g++.exe -Wall -fexceptions -O2  -c xxd.cpp -o xxd.o
mingw32-g++ (tdm-1) 4.7.1
mingw32-g++.exe -Wall -fexceptions -O2  -c xxd.cpp -o xxd.o

https://rot47.net/94 181KB 94 C++ Coding Exercise - xxd - make hex dump on Windows c / c++ tools / utilities

Github Full Source Code

Fork, Improve, and Make a Pull Request!

https://github.com/DoctorLai/xxd/blob/master/main.cpp

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
1289 words
Last Post: Providing an IsUnicodeVersion API in Your Delphi Project
Next Post: PHP Script to Execute MySQL statements in a text file

The Permanent URL is: C++ Coding Exercise – xxd – make hex dump on Windows

Leave a Reply