在C++中能否定义Shift-JIS字符串字面量?求更简便实现方案
问题
我正在编写一个涉及比较Shift-JIS编码字符串的程序,仅需与几个固定值进行比对,希望将这些值编码为字符串常量/字面量。我已经通过iconv或MultiByteToWideChar/WideCharToMultiByte函数实现了自定义字面量,但想了解是否存在其他实现方法,或更简便的自定义字面量定义方式。
背景信息
- 程序仅针对Windows平台编译
- 使用Mingw64交叉编译器从Linux环境构建
- 待比对的字符串均为16字节固定长度
- 可使用C17特性,但无法使用C20特性
当前实现代码
auto operator "" _sjs(const char* in, std::size_t len) { #ifdef HAS_ICONV auto t = iconv_open("SHIFT-JIS", "UTF-8"); // max length of BS header name + 1 for terminator char output[17] = ""; memset(output, ' ', 16); size_t outLeft = 16; char* tmpOut = output; iconv(t, &in, &len, &tmpOut, &outLeft); iconv_close(t); return std::string{output}; #else wchar_t output[34]; memset(output, 0, 34); MultiByteToWideChar(CP_UTF8, 0, in, -1, output, len); char output2[17] = ""; memset(output2, ' ', 16); WideCharToMultiByte(932, 0, output, -1, output2, len*2, NULL, NULL); return std::string{output2}; #endif }
可行优化方案
1. 预编码为二进制字面量(最简便直接)
既然待比对的是固定16字节的Shift-JIS字符串,完全可以提前把这些字符串转换成Shift-JIS字节序列,直接用二进制字面量定义成std::array<char,16>或者字符数组常量,不需要运行时转换:
比如,先通过Linux工具生成目标字符串的Shift-JIS十六进制字节(命令示例:echo "目标字符串" | iconv -f UTF-8 -t SHIFT-JIS | xxd -i),再定义常量:
constexpr std::array<char,16> SJIS_TARGET = { 0x83,0x65,0x83,0x58,0x83,0x67,0x82,0xB7, 0x82,0xEA,0x82,0xC5,0x20,0x20,0x20,0x20 // 补空格到16字节 };
这种方式在编译期就完成编码转换,运行时直接用常量比对,无性能开销,也省去了自定义字面量的运行时转换逻辑。
2. 简化自定义字面量的实现逻辑
如果坚持要用自定义字面量,可以优化现有代码的冗余和错误处理:
- 统一空格填充逻辑,避免重复代码
- 增加转换失败的错误检查,避免非法结果
- 明确返回固定16字节的字符串
优化后的代码示例:
#include <string> #include <cstring> #include <stdexcept> std::string operator "" _sjs(const char* in, std::size_t len) { char output[17] = {0}; std::memset(output, ' ', 16); // 统一先填充空格到16字节 #ifdef HAS_ICONV iconv_t cd = iconv_open("SHIFT-JIS", "UTF-8"); if (cd == (iconv_t)-1) { throw std::runtime_error("Failed to initialize iconv"); } size_t in_left = len; char* in_ptr = const_cast<char*>(in); size_t out_left = 16; char* out_ptr = output; if (iconv(cd, &in_ptr, &in_left, &out_ptr, &out_left) == (size_t)-1) { iconv_close(cd); throw std::runtime_error("Shift-JIS conversion failed"); } iconv_close(cd); #else wchar_t wide_buf[34] = {0}; int wide_len = MultiByteToWideChar(CP_UTF8, 0, in, static_cast<int>(len), wide_buf, 33); if (wide_len == 0) { throw std::runtime_error("UTF-8 to wide char conversion failed"); } int sjis_len = WideCharToMultiByte(932, 0, wide_buf, wide_len, output, 16, nullptr, nullptr); if (sjis_len == 0) { throw std::runtime_error("Wide char to Shift-JIS conversion failed"); } #endif return std::string(output, 16); // 确保返回固定16字节长度 }
3. 利用Mingw64的编码扩展特性
Mingw64支持通过编译器属性直接指定字符串字面量的编码,适合字符串数量少的场景:
// 需确保源代码文件编码与指定编码匹配,或编译器能正确识别 const char* target_sjis __attribute__((encoding("Shift-JIS"))) = "目标文字列 "; // 手动补空格到16字节
注意这种方式依赖编译器扩展,移植性稍差,但对于固定平台的简单场景来说非常简便。
内容的提问来源于stack exchange,提问作者Robert Stewart
相关产品推荐
相关产品推荐

