본문 바로가기
프로그래밍/C++

C++ ] Format-Hex 명령어(Hex 덤프) 직접 구현

by eteo 2023. 5. 29.

 

 

 

 

 

 

 

 

 

 

#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>

using namespace std;
namespace fs = filesystem;

void printhex(char *path) {

    // ios::binary 플래그를 사용해 이진모드로 파일 열기
    ifstream file(path, ios::binary);
    if (!file) {
        cerr << "failed to open file: " << path << endl;
        return;
    }

    cout << endl << endl;
    cout << "file: " << path << endl;
    cout << endl;
    cout << "offset(h)  ";

    for (int i = 0; i < 16; i++)
    {
        cout << setw(2) << setfill('0') << hex << uppercase << i << " ";
    }

    cout << " ascii" << endl;
    cout << "---------  ";
    for (int i = 0; i < 16; ++i) {
        cout << "-- ";
    }
    cout << " ----------------" << endl;

    // 한줄에 16 Byte 출력. offset은 uint32_t
    unsigned int offset = 0;
    // 헥사값을 담을 버퍼는 uint8_t, 배열로 해도 된다.
    // 벡터는 동적으로 메모리를 할당하지만 생성시 아래처럼 요소 개수를 지정하면 연속된 메모리에 배치된다.
    vector<unsigned char> buffer(16);
    // null 포함 아스키값 담을 버퍼
    char ascii[17] = {0, };

    while (true)
    {
        // vector.data()는 벡터의 시작 주소를 반환한다
        // read() 읽어온 데이터를 저장할 문자열 버퍼의 포인터인 char* s 와 읽어올 문자 수인 streamsize n 을 인자로 받는다.
        file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
        // file.gcount()는 최근 read() 호출로 읽은 바이트 수를 반환한다.
        int bytesread = file.gcount();
        if (bytesread == 0) break;
        // 16진수 대문자 출력, 출력 폭은 8, 나머지는 '0'으로 채운다.
        cout << setw(8) << setfill('0') << hex << uppercase << offset << "   ";

        for (int i = 0; i < bytesread; i++)
        {
            // char를 int로 캐스팅. 아직 버퍼 비우기 전이라 출력형식은 그대로 내려온다.
            cout << setw(2) << static_cast<int>(buffer[i]) << " ";
            // 32(공백 문자)부터 126('~' 문자)까지는 출력 가능한 문자(isprint())로 그대로 출력하고 아닌경우 '.' 출력
            ascii[offset % 16] = (buffer[i] >= 32 && buffer[i] <= 126) ? buffer[i] : '.';
            offset++;
        }

        if (offset % 16 == 0)
        {
            ascii[16] = '\0';
        }
        else
        {
            ascii[offset % 16] = '\0';
            for(int i = 0; i < 16 - (offset % 16) ; i++) cout << "   ";
        }

        cout << ascii << endl;
    }
    
    file.close();
    cout << endl << endl;
}


int main(int argc, char* argv[]) {

    // 인자 개수 확인
    if (argc < 2) {
        cout << "usage: " << argv[0] << " <filename>" << endl;
        return 1;
    }

    printhex(argv[1]);

    return 0;
}