C/C++字符串编码
MSVC++编译器本身支持的源文件编码是本地编码、带BOM的UTF-8、UTF-16LE、UTF-16BE,不支持不带BOM的UTF-8,会被误认为BGK这样的本地编码,所以字符串会直接被复制到程序中,不进行执行字符集的转换。所以会出现下面这种情况,程序中出现了UTF-8编码的字符串,但是xxA函数将其作为GBK编码进行解码:
如果源文件是本地编码、带BOM的UTF-8、UTF-16LE、UTF-16BE等编码,则会被正确以本地编码嵌入程序:
事实上,我们可以单独指定编译器的源码编码和执行字符集:
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>") #指定源文件编码
这时之前UTF-8的识别错误就修复了:
可以指定执行字符集为utf-8,这样就意味着放弃了xxA函数,不过可以使用CP_UTF8代码页转为UTF-16字符串。
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/execution-charset:utf-8>") #指定执行字符集
MultiByteToWideChar(CP_UTF8, 0, str, -1, buf, cBuf) // 使用代码页转换为宽字符字符串
兼容xxA函数和xxW函数的做法
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>") #指定源文件编码为最常用的编码UTF-8
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/execution-charset:gbk>") #使用系统默认代码页GBK来兼容xxA函数
然后,使用以下函数转换UTF-16字符串:
#include "wchar.h"
#include <Windows.h>
#define CP_utf8to16 CP_UTF8
wchar_t*
utf8to16(const char* str) {
if (str == NULL) return L"(null)";
// 计算缓冲区需要的大小, 如果函数成功, 则返回 UTF-8 字符数量, 所以无法确定具体字节数
int cBuf = MultiByteToWideChar(CP_utf8to16, 0, str, -1, NULL, 0);
if (cBuf == 0) return L"(null)";
wchar_t* buf = (wchar_t*)malloc(cBuf * 4);
if (cBuf != MultiByteToWideChar(CP_utf8to16, 0, str, -1, buf, cBuf)) return L"(null)";
return buf;
}
#define CP_asciito16 CP_ACP
wchar_t*
asciito16(const char* str) {
if (str == NULL) return L"(null)";
// 计算缓冲区需要的大小, 如果函数成功, 则返回 UTF-8 字符数量, 所以无法确定具体字节数
int cBuf = MultiByteToWideChar(CP_asciito16, 0, str, -1, NULL, 0);
if (cBuf == 0) return L"(null)";
wchar_t* buf = (wchar_t*)malloc(cBuf * 4);
if (cBuf != MultiByteToWideChar(CP_asciito16, 0, str, -1, buf, cBuf)) return L"(null)";
return buf;
}
char*
utf16to8(const wchar_t* str) {
if (str == NULL) return "(null)";
// 计算缓冲区需要的大小, 如果函数成功, 则返回具体字节数, 所以 cBuf 至少是 1 (UTF-8以0x00结尾)
int cBuf = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
if (cBuf < 1) return "(null)";
char* buf = (char*)malloc(cBuf); // 分配缓冲区
if (cBuf != WideCharToMultiByte(CP_UTF8, 0, str, -1, buf, cBuf, NULL, NULL)) return "(null)";
return buf;
}
参考以下函数:
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/wcstombs-wcstombs-l?view=msvc-170
https://docs.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-widechartomultibyte
Rust 与 C++ 之间的字符串传递
第一,使用UTF-8互传,根据需要由C++选择是否转换为UTF-16。
第二,由于Rust支持UTF-16,可以直接调用xxW函数。
#[link(name = "User32")]
extern "C" {
fn MessageBoxW(hWnd: u64, lpText: *const u8, lpCaption: *const u8, uType: u32) -> u32;
}
fn main() {
unsafe {
let str_utf16: Vec<u16> = "你好\0".encode_utf16().collect();
let ptr = str_utf16.as_ptr() as *const u8;
MessageBoxW(0, ptr, "A\0B\0\0\0".as_ptr(), 0);
}
}
不过,C++传递给Rust的裸指针怎么转为Rust字符串呢?可以先使用std::slice::from_raw_parts
将指针转为切片,再使用String::from_utf8
或者from_utf16
转为字符串对象。
fn main() {
unsafe {
let str_utf16 = "你好\0".encode_utf16();
let str_utf16: Vec<u16> = str_utf16.collect();
let ptr = str_utf16.as_ptr() as *const u16;
// MessageBoxW(0, ptr, "A\0B\0\0\0".as_ptr(), 0);
let a = std::slice::from_raw_parts(ptr, 3);
let s = String::from_utf16(a).unwrap();
MessageBoxA(0, s.as_ptr(), "A\0B\0\0\0".as_ptr(), 0);
}
}
Node.js ffi-napi 传递UTF-16字符串
const ffi = require('ffi-napi');
function L(text) {
return Buffer.from(text + '\0', 'utf16le');//.toString('binary');
};
// 通过ffi加载User32.dll
const myUser32 = new ffi.Library('User32', {
'MessageBoxW': // 声明这个dll中的一个函数
[
'int32', ['int32', 'string', 'string', 'int32'], // 用json的格式罗列其返回类型和参数类型
],
});
// 调用user32.dll中的MessageBoxW()函数, 弹出一个对话框
const isOk = myUser32.MessageBoxW(
0, 'H\0e\0l\0l\0o\0\0', L`你好`, 1
);
console.log(isOk);
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· 【.NET】调用本地 Deepseek 模型
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)
2021-03-15 Spring 5 新特性
2019-03-15 SSH HTTP代理