如何用C++实现自定义Base64与BaseN双向编码解码?
Got it, let's break this down into manageable parts. You need a C++ solution that can convert between a Base64 string (like "IloveC0mpil3rs") and a custom BaseN string (e.g., Base4's "10230102010301"), plus reverse the process. The key here is to separate the Base64 handling from the generic BaseN logic, then combine them. Here's how to do it:
Before diving into code, let's clarify the flow:
- Base64 → Custom BaseN: First decode the Base64 string into raw bytes, then encode those bytes into your target BaseN format.
- Custom BaseN → Base64: Reverse the above: decode the BaseN string into raw bytes, then encode those bytes back to Base64.
This separation keeps the code modular and reusable for other Base types later.
First, let's build a reliable Base64 utility. We'll use the standard Base64 character set and handle padding correctly.
#include <string> #include <vector> #include <unordered_map> // Standard Base64 character set and reverse lookup table const std::string BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; const std::unordered_map<char, int> BASE64_REVERSE = [](){ std::unordered_map<char, int> map; for(int i = 0; i < BASE64_CHARS.size(); ++i){ map[BASE64_CHARS[i]] = i; } map['='] = 0; // Padding character return map; }(); // Encode raw bytes to Base64 std::string base64_encode(const std::vector<unsigned char>& bytes) { std::string result; int i = 0; unsigned char buffer[3]; for(const auto& byte : bytes) { buffer[i++] = byte; if(i == 3) { result += BASE64_CHARS[(buffer[0] & 0xFC) >> 2]; result += BASE64_CHARS[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; result += BASE64_CHARS[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; result += BASE64_CHARS[buffer[2] & 0x3F]; i = 0; } } // Handle padding for remaining bytes if(i > 0) { for(int j = i; j < 3; ++j) buffer[j] = 0; result += BASE64_CHARS[(buffer[0] & 0xFC) >> 2]; result += BASE64_CHARS[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)]; result += (i == 1) ? '=' : BASE64_CHARS[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)]; result += '='; } return result; } // Decode Base64 string to raw bytes std::vector<unsigned char> base64_decode(const std::string& base64_str) { std::vector<unsigned char> result; int i = 0; unsigned char buffer[4]; for(const auto& c : base64_str) { if(c == '=') break; // Stop at padding buffer[i++] = BASE64_REVERSE.at(c); if(i == 4) { result.push_back((buffer[0] << 2) | ((buffer[1] & 0x30) >> 4)); result.push_back(((buffer[1] & 0x0F) << 4) | ((buffer[2] & 0x3C) >> 2)); result.push_back(((buffer[2] & 0x03) << 6) | buffer[3]); i = 0; } } // Handle remaining bytes if(i > 0) { if(i == 2) { result.push_back((buffer[0] << 2) | ((buffer[1] & 0x30) >> 4)); } else if(i == 3) { result.push_back((buffer[0] << 2) | ((buffer[1] & 0x30) >> 4)); result.push_back(((buffer[1] & 0x0F) << 4) | ((buffer[2] & 0x3C) >> 2)); } } return result; }
Next, let's build a flexible BaseN utility that works with any custom character set (as long as the set length matches your target base, e.g., 4 characters for Base4).
#include <algorithm> #include <stdexcept> // Encode raw bytes to custom BaseN string std::string baseN_encode(const std::vector<unsigned char>& bytes, const std::string& charset) { int base = charset.size(); if(base < 2) throw std::invalid_argument("Base must be at least 2"); // Convert bytes to a big integer (using vector<uint64_t> for large values) std::vector<uint64_t> num; num.push_back(0); for(const auto& byte : bytes) { // Multiply current number by 256 and add the byte uint64_t carry = byte; for(auto& digit : num) { uint64_t product = digit * 256 + carry; digit = product % base; carry = product / base; } while(carry > 0) { num.push_back(carry % base); carry /= base; } } // Convert the reversed digits to the charset std::string result; for(auto it = num.rbegin(); it != num.rend(); ++it) { result += charset[*it]; } // Handle empty input case return result.empty() ? "" : result; } // Decode custom BaseN string to raw bytes std::vector<unsigned char> baseN_decode(const std::string& baseN_str, const std::string& charset) { int base = charset.size(); if(base < 2) throw std::invalid_argument("Base must be at least 2"); // Create reverse lookup for charset std::unordered_map<char, int> char_to_val; for(int i = 0; i < charset.size(); ++i) { char_to_val[charset[i]] = i; } // Convert BaseN string to a big integer std::vector<uint64_t> num; num.push_back(0); for(const auto& c : baseN_str) { int val = char_to_val.at(c); // Multiply current number by base and add the value uint64_t carry = val; for(auto& digit : num) { uint64_t product = digit * base + carry; digit = product % 256; carry = product / 256; } while(carry > 0) { num.push_back(carry % 256); carry /= 256; } } // Convert digits to bytes (reverse since we stored least significant first) std::vector<unsigned char> result; for(auto it = num.rbegin(); it != num.rend(); ++it) { result.push_back(static_cast<unsigned char>(*it)); } // Remove leading zero bytes (if any, except for empty input) if(!result.empty()) { auto first_non_zero = std::find_if(result.begin(), result.end(), [](unsigned char b){ return b != 0; }); if(first_non_zero != result.end()) { result = std::vector<unsigned char>(first_non_zero, result.end()); } else { result = {0}; // All zeros, keep one zero byte } } return result; }
Now let's use these utilities to do exactly what you need: convert between Base64 and your custom Base4 string.
#include <iostream> int main() { // Example input std::string base64_input = "IloveC0mpil3rs"; std::string base4_charset = "0123"; // Custom Base4 character set // Step 1: Base64 → Raw Bytes std::vector<unsigned char> raw_bytes = base64_decode(base64_input); // Step 2: Raw Bytes → Base4 String std::string base4_output = baseN_encode(raw_bytes, base4_charset); std::cout << "Base64 to Base4: " << base4_output << std::endl; // Step 3: Base4 → Raw Bytes (reverse) std::vector<unsigned char> reversed_raw = baseN_decode(base4_output, base4_charset); // Step 4: Raw Bytes → Base64 (reverse) std::string reversed_base64 = base64_encode(reversed_raw); std::cout << "Base4 back to Base64: " << reversed_base64 << std::endl; return 0; }
- Charset Validity: Make sure your BaseN charset has exactly
Nunique characters (no duplicates). For Base4, that's 4 distinct characters like "0123". - Empty Strings: The code handles empty inputs gracefully, but you might want to add explicit checks if needed.
- Large Data: The big integer implementation uses
vector<uint64_t>which can handle very large byte streams without overflow. For extreme cases, you could optimize further, but this works for most practical uses. - Padding: Base64 uses
=for padding, which our decoder handles correctly. Custom BaseN doesn't need padding unless you add it explicitly (the current code doesn't include padding for BaseN).
内容的提问来源于stack exchange,提问作者user9507127

