NodeJS向C++ node-addon-api模块传递非ASCII文件名失败排查
问题
需从NodeJS向基于node-addon-api开发的C模块传递含ä、ö、ü等非ASCII字符的文件路径。单独运行C代码时可正确识别文件,但通过NodeJS调用C++模块时,含特殊字符的路径会被判定为文件不存在,日志却能写入正确文件名。
相关代码
C++独立测试代码
// PathJSON.h
#pragma once #include <filesystem> #include <format> #include <fstream> #include <string> namespace test_json { std::string checkPathInJSON(const char* json) { std::string pathString = json; std::filesystem::path p(pathString); std::string outString = ""; if (std::filesystem::exists(p)) { outString = std::format("{};exists", pathString); } else { outString = std::format("{};MISSING", pathString); } std::filesystem::path logPath("d:/filelog.csv"); std::ofstream ofs(logPath, std::ios::app); if (ofs.is_open()) { ofs << outString << std::endl; ofs.close(); } return outString; } }
// Central.cpp
#include <iostream> #include "PathJSON.h" int main() { std::string str = test_json::checkPathInJSON("D:/foo.txt"); std::cout << str << std::endl; // D:/foo.txt;exists str = test_json::checkPathInJSON("D:/föö.txt"); std::cout << str << std::endl; // D:/föö.txt;exists }
单独运行C++代码的日志输出(正确):
D:/foo.txt;exists // correct D:/föö.txt;exists // correct
NodeJS调用C++模块代码
// hello.cc
#include <iostream> #include <napi.h> #include "PathJSON.h" Napi::String Method(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string str = ""; if(info.Length() == 1 && info[0].IsString()) { std::string par = info[0].As<Napi::String>(); std::cout << par << std::endl; str = test_json::checkPathInJSON(par.c_str()); } return Napi::String::New(env, str); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, Method)); return exports; } NODE_API_MODULE(node_api_1, Init);
// hello.js
console.log(addon.hello("D:/foo.txt")); console.log(addon.hello("D:/föö.txt"));
通过NodeJS调用后的日志输出(异常):
D:/foo.txt;exists // correct D:/föö.txt;MISSING // ERROR
尝试过用rapidjson传递数据、修改路径写法(如D:\\föö.txt),问题仍未解决。
解决方案
问题核心是NodeJS字符串为UTF-8编码,而Windows平台下std::filesystem默认使用系统本地编码(如GBK或UTF-16),直接将UTF-8转成std::string传递会导致非ASCII字符编码错乱,文件路径无法被正确识别。
修复步骤
- 在C++模块中使用宽字符串处理路径
Node-API提供Napi::String::Utf16Value()方法可直接获取UTF-16编码的宽字符串,避免编码转换丢失信息。 - 修改路径检查函数支持宽字符输入
调整函数参数为const wchar_t*,使用std::wstring和std::filesystem::path的宽字符构造方法。
修改后的代码
调整PathJSON.h
#pragma once #include <filesystem> #include <format> #include <fstream> #include <string> #include <locale> #include <codecvt> namespace test_json { // 新增宽字符版本的路径检查函数 std::wstring checkPathInJSONW(const wchar_t* json) { std::wstring pathString = json; std::filesystem::path p(pathString); std::wstring outString = L""; if (std::filesystem::exists(p)) { outString = std::format(L"{};exists", pathString); } else { outString = std::format(L"{};MISSING", pathString); } std::filesystem::path logPath(L"d:/filelog.csv"); std::wofstream ofs(logPath, std::ios::app); if (ofs.is_open()) { ofs << outString << std::endl; ofs.close(); } return outString; } // 保留原函数兼容其他调用场景(可选) std::string checkPathInJSON(const char* json) { std::string pathString = json; std::filesystem::path p(pathString); std::string outString = ""; if (std::filesystem::exists(p)) { outString = std::format("{};exists", pathString); } else { outString = std::format("{};MISSING", pathString); } std::filesystem::path logPath("d:/filelog.csv"); std::ofstream ofs(logPath, std::ios::app); if (ofs.is_open()) { ofs << outString << std::endl; ofs.close(); } return outString; } }
修改hello.cc
#include <iostream> #include <napi.h> #include "PathJSON.h" #include <locale> #include <codecvt> // 工具函数:将宽字符串转为UTF-8字符串,返回给NodeJS std::string wstring_to_utf8(const std::wstring& wstr) { std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; return converter.to_bytes(wstr); } Napi::String Method(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); std::string str = ""; if(info.Length() == 1 && info[0].IsString()) { // 获取NodeJS传递的UTF-16宽字符串 std::wstring par = info[0].As<Napi::String>().Utf16Value(); std::wstring w_result = test_json::checkPathInJSONW(par.c_str()); // 转成UTF-8格式返回给NodeJS str = wstring_to_utf8(w_result); } return Napi::String::New(env, str); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "hello"), Napi::Function::New(env, Method)); return exports; } NODE_API_MODULE(node_api_1, Init);
关键说明
- UTF-16是Windows原生路径编码,NodeJS内部字符串也以UTF-16存储,直接用
Utf16Value()获取宽字符串可避免编码转换错误。 - 使用
std::wofstream写入日志,确保宽字符能正确输出到文件。 - 最终将宽字符串转回UTF-8返回给NodeJS,保证JS端能正常显示结果。
内容的提问来源于stack exchange,提问作者Markstar
相关产品推荐
相关产品推荐

