Tìm giải pháp Decode Hex to String trong C++

Em đang tìm cách Decode UTF-16 Hex tới String, nhưng tìm mãi không có cách giải quyết mong mọi người giúp đỡ.

Em xin trình bày vấn đề :

  • Đầu tiên em có 2 Mã Hex:
  • UTF-8 Hex : 4875E1BBB36E68204E67E1BB8D632048C3A0204D79
  • UTF-16 Hex : 004800751EF3006E00680020004E00671ECD00630020004800E00020004D0079

Em có viết chương trình C++ để decode từ UTF-16 Hex sang String nhưng kết quả lại chạy ra không đúng, đây là chương trình của em

        #include <string> 
        #include <sstream>
       #include <fstream>
       #include <iostream>
       #include <ios>


int main(){


std::basic_string<uint16_t> bytes;
std::string hex = "004800751EF3006E00680020004E00671ECD00630020004800E00020004D0079";
//std::string hex = "4875E1BBB36E68204E67E1BB8D632048C3A0204D79";
 
for (size_t i = 0; i < hex.length(); i += 2)
{
uint16_t byte;

std::string nextbyte = hex.substr(i, 2);
std::istringstream(nextbyte) >> std::hex >> byte;

bytes.push_back(static_cast<uint8_t>(byte));
}

std::string result(bytes.begin(), bytes.end());

std::ofstream file("file.bin", std::ios::binary| std::ios::out);
if(file.is_open())  {
    file<<result;
    file.close();
}else{
    std::cout<<"File error!!"<<std::endl;
}

std::string value = hex_to_string(hex);
std::cout<<"Value : "<<value<<std::endl;

 return 0; }

Kết quả chạy từ chương trình với UTF-16 Hex
Capture6

Kết quả chạy từ chương trình với UTF-8 Hex
Capture7

Mong mọi người có giải pháp hay cách nào khác giúp em với ạ.

Em cảm ơn.

em chạy chương trình trên Linux à :V

std::string trong C++ thì mỗi phần tử có kiểu là char, ở đây có lẽ là char 8-bit rồi. Mà 8-bit thì làm sao chứa được UTF-16 có mỗi phần tử là ký tự 16-bit :V

em phải viết thêm hàm encode chuỗi UTF-16 thành chuỗi UTF-8 rồi mới output chuỗi utf-8 đó được :V

4 Likes

UTF-8UTF-16 cách mã hóa khác nhau mà bạn. Phía trên thấy bạn chỉ cắt đúng 2 kí tự từ chuỗi hex (1 byte), nhưng UTF-16 cần đến 2 byte (4 kí tự từ chuỗi hex).
Mỗi kí tự UTF-8 có thể lưu từ 1 byte đến 4 byte tùy theo giá trị của từng kí tự.
Nói thêm là còn có UTF-32 thì dùng 4 byte để lưu 1 kí tự. Đảm bảo một hệ thống Unicode đủ chỗ cho mọi kí tự trên thế giới.
Kí tự cuối cùng trong bảng mã Unicode (tính đến thời điểm này) dưới tất cả dạng mã hóa:

Undefined Character (U+10FFFF)
UTF-8 Encoding: 0xF4 0x8F 0xBF 0xBF
UTF-16 Encoding: 0xDBFF 0xDFFF
UTF-32 Encoding: 0x0010FFFF

https://www.compart.com/en/unicode/U+10ffff

Như chữ trong tiếng Việt thì UTF-16 có giá trị là 1EA0, nhưng UTF-8 thì là E1BAA0 (3 byte).

Bạn để ý đầu 2 chuỗi có giống và khác:

004800751EF3...
4875E1BBB3...

Đó chính là 3 kí tự đầu tiên Huỳ trong chuỗi của bạn.
Như bạn có thấy chúng có khác biệt không?

Chữ H u Số byte
UTF-8 48 75 E1BBB3 1 hoặc 3 byte
UTF-16 0048 0075 1EF3 Cố định 2 byte

Do chuỗi ở dạng UTF-16 bạn cắt mỗi lần 1 byte nên nó trở thành
Mảng byte: \x00\x48\x00\x75\x1e\0xf3
Sau đó gọi đến std::string result(bytes.begin(), bytes.end()); chuỗi được xử lý mã hóa UTF-8 nên nó ra vậy. Ừ, đến đây nó lại xử lý mảng byte dạng UTF-8 đấy.

Bạn thử vào trang https://dencode.com/string/hex và dán chuỗi UTF-16, nhưng chọn mã hóa UTF-8 xem kết quả có giống y như lỗi của bạn hay không?
https://dencode.com/string/hex?v=004800751EF3006E00680020004E00671ECD00630020004800E00020004D0079&oe=UTF-8&nl=crlf

Cách giải quyết là đối với UTF-16 thì nó nên cắt đúng 2 byte (4 kí tự chuỗi hex) và sau đó chuyển về UTF-8. Ờ, đoạn này hơi khó, chả biết có thư viện chuẩn nào hỗ trợ không.

std::string lưu chuỗi trong bộ nhớ dạng UTF-8. Xem thử nhé:

5 Likes

Giải pháp đây nhé !

UTF16 thì sử dụng mảng wchar_t và wstring thay cho mảng char và string.

    std::string input = "004800751EF3006E00680020004E00671ECD00630020004800E00020004D0079";
    int len = input.length();
    if(len % 4 != 0) return 1; // kiểm tra độ dài có chẵn 4 không
    int cvtlen = len / 4 + 1;
    wchar_t *wbuff = new wchar_t[cvtlen];
    wchar_t *ptr = wbuff;
    wmemset(wbuff,0,cvtlen);
    for(int i=0;i<len;i+=4){
        std::string nextbyte = input.substr(i,4);
        *ptr = (wchar_t)std::stoi(nextbyte,nullptr,16);
        ptr++;
    }
    std::wcout << wbuff << std::endl; 
    // thử in trên console -> toạch.
    // biết có thể fix để in nhưng ngại. Có thể code của bạn đúng nhưng việc in utf16 trên console mặc định không hỗ trợ nên nhìn thấy sai chưa chắc đã sai.
    MessageBoxW(nullptr,wbuff,L"",0);
    // show lên messagebox -> chính xác. Vậy là xong.
    delete [] wbuff;

5 Likes

Em chạy test code trên page này á anh https://www.onlinegdb.com/online_c++_compiler. :smiley:
Cảm ơn anh đã chia sẽ.

1 Like

Em là newbie nên cũng chưa có kinh nghiệm nhiều,

Cảm ơn Anh đã chia sẽ rất nhiều về kiến thức, em sẽ follow và chỉnh sửa lại program.

Thank you very much.

1 Like

Em cảm ơn anh rất nhiều ạ.
Em sẽ refer và chỉnh lại program.

Thank you very much.

1 Like

Anh ơi, anh có thể hướng dẫn em cách để write wchar_t tới file bin được không ạ,
Hiện tại em dùng code anh chạy Ok lắm ạ. em debug thì thấy đã convert được rồi ạ.

Nhưng em write file bin thì dữ liệu nó bị sai ạ,

Mình test đoạn code lưu file trên đã hoạt động đúng rồi nhé !
Có thể do trình mở file nó không hỗ trợ hoặc mở không đúng cách thôi (nó đang mở ở kiểu ANSI).
Thử mở bằng notepad, vscode… xem sao.
image

3 Likes

Em cảm ơn anh.

Anh ơi, do dự án em làm dùng C++03/98 nên không có hổ trợ hàm stoi như code của anh, em tìm trên google thì thấy có hàm strtoul nó khá giống với hàm stoi

nên em chạy nó ghi ra file nhưng mà nó bị lỗi như trong hình, em mở bằng notepad++

Bạn chỉnh kiểu mã hóa của Notepad++ sang UTF-16LE nhé. Hình như là Thanh công cụ > Endcoding > Chọn kiểu mã hóa.
Mặc định nó luôn đọc ở dạng ANSI, trừ khi đầu tập tin có kí tự BOM để nhận dạng kiểu mã hóa thì nó sẽ tự động đọc đúng kiểu.

3 Likes

Hàm std::stoi mục đích là để chuyển một chuỗi dạng hex 16 bit về một số int thôi. Mình tìm hàm nào chức năng tương tự hoặc tự viết cũng được.

Để chắc chắn nhất việc lưu có đúng chưa với dung lượng file nhỏ em có thể tải phần mềm binary viewer về để xem dữ liệu binary của một tập tin bất kỳ.
Như hình dưới là tập tin bin lưu “Huỳnh Ngọc Hà My” với định dạng UTF-16.
Em kiểm tra xem dữ liệu có khớp vậy không.
Nếu khác thì kiểm tra còn nếu giống thì là do phần mềm mở thôi.
image

3 Likes

Em mở lên được rồi ạ , Em cảm ơn anh nhiều ạ

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?