从具有非 ASCII 字符的 wchar_t 创建 v8::String 的安全方法是什么?
What is the safe way to create a v8::String from a wchar_t with non-ASCII characters?
我正在为 DAB development board 编写一个 Node.js 前端,它最终将在 Raspberry Pi 上 运行。我是一名 Java 和 Web 开发人员,我正在努力使用 C++ 并在不同类型的字符串之间进行转换。
DAB板自带C++ SDK,有很多方便的功能。它允许我使用 GetTotalProgram()
获取可用程序的数量。对于每个程序,我都可以调用 GetProgramName
来获取程序名称:
GetProgramName(char mode, long dabIndex, char namemode, wchar_t * programName)
... 其中 mode
表示 FM
或 DAB
,namemode
表示长名称或短名称。程序的名称将在 programName
.
中返回
为了将 wchar_t *programName
转换为 v8::String
,我找到了我正在使用的这个片段,并了解了以下基础知识:
wchar_t buff[300];
char cbuff[600];
GetProgramName(0, i, 1, buff);
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
我遍历可用程序并构建 v8::Array
:
void GetPrograms(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
wchar_t buff[300];
char cbuff[600];
int numberOfPrograms, i;
numberOfPrograms = GetTotalProgram();
Local<v8::Array> ARRAY = Array::New(isolate, totalprogram);
for (i = 0; i < numberOfPrograms; i++) {
if (GetProgramName(0, i, 1, buff)) {
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
Local<Object> obj = Object::New(isolate);
obj->Set(String::NewFromUtf8(isolate, "name"), str);
ARRAY->Set(i, obj);
}
}
args.GetReturnValue().Set(ARRAY);
}
我从我的 Node 应用程序调用 C++ 方法:
var programs = ext.getPrograms();
for (var i = 0; i < programs.length; i++) {
console.log(programs[i][name]);
}
这主要是可行的,但是当程序名称包含非 ASCII 字符时,例如 Æ
、Ø
、Å
,next ARRAY 中的元素有一个乏味的名称。
与预期输出相比,节点代码段实际输出 (console.log
) 如下:
| ACTUAL | EXPECTED |
| --------- | ---------- |
| NRK SUPER | NRK SUPER |
| NRK VUPER | NRK VÆR |
| NRK P1 ER | NRK P1 |
似乎非 ASCII 字符导致下一个 wcstombs
提前退出,而不是复制后面的字符。
为什么会这样?有没有更好的方法从我的 wchar_t
创建一个 v8::String
?
注:
当 运行 在 Raspberry Pi 上时,我现在已经能够将这个问题隔离到 wcstombs
方法。以下代码:
#include <stdio.h>
#include <string>
#include <cstring>
#include <cstdlib>
char cbuff[600];
wchar_t buff[300] = L"ABCø123abc";
int main( int argc, const char* argv[] ) {
wcstombs( cbuff, buff, wcslen(buff) );
wprintf(L"wcslen of wchar_t array: %u - strlen of char array: %u\n", (char) wcslen(buff), strlen(cbuff));
}
当 运行 在 Mac 上时,输出
wcslen of wchar_t array: 10 - strlen of char array: 10
,
但是当 运行 在 Raspberry 上时,输出
wcslen of wchar_t array: 10 - strlen of char array: 3
- 也就是说,它只计算 ø
字符
之前的字符
这看起来类似于 this unanswered question。
我猜 wcstombs 中的最后一个参数是问题的原因。而不是尝试
wcstombs( cbuff, buff, wcslen(buff) );
尝试
memset(cbuff, 0, sizeof(cbuff));
wcstombs( cbuff, buff, sizeof(cbuff) );
问题出在 wcstombs( cbuff, buff, wcslen(buff) )
调用中,它会在遇到非 ASCII 字符时停止复制字符。 docs 表示 此函数的行为取决于所选 C 语言环境的 LC_CTYPE 类别。
因此将语言环境设置为 UTF-8 变体解决了问题:
setlocale(LC_CTYPE, "C.UTF-8");
完成此操作后,我现在可以这样创建 v8::String
:
wchar_t buff[300] = L"Something non-ASCII ÆØÅ here";
char cbuff[600];
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
WCHAR str[256];0
... // fill str array here
Local<String> v8str = String::NewFromTwoByte(isolate, (const uint16_t *) str);
注意 ::NewFromTwoByte
用法而不是 ::NewFromUtf8
和 (const uint16_t *)
转换。
::NewFromTwoByte
Allocates a new string from UTF-16 data.
我正在为 DAB development board 编写一个 Node.js 前端,它最终将在 Raspberry Pi 上 运行。我是一名 Java 和 Web 开发人员,我正在努力使用 C++ 并在不同类型的字符串之间进行转换。
DAB板自带C++ SDK,有很多方便的功能。它允许我使用 GetTotalProgram()
获取可用程序的数量。对于每个程序,我都可以调用 GetProgramName
来获取程序名称:
GetProgramName(char mode, long dabIndex, char namemode, wchar_t * programName)
... 其中 mode
表示 FM
或 DAB
,namemode
表示长名称或短名称。程序的名称将在 programName
.
为了将 wchar_t *programName
转换为 v8::String
,我找到了我正在使用的这个片段,并了解了以下基础知识:
wchar_t buff[300];
char cbuff[600];
GetProgramName(0, i, 1, buff);
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
我遍历可用程序并构建 v8::Array
:
void GetPrograms(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
wchar_t buff[300];
char cbuff[600];
int numberOfPrograms, i;
numberOfPrograms = GetTotalProgram();
Local<v8::Array> ARRAY = Array::New(isolate, totalprogram);
for (i = 0; i < numberOfPrograms; i++) {
if (GetProgramName(0, i, 1, buff)) {
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
Local<Object> obj = Object::New(isolate);
obj->Set(String::NewFromUtf8(isolate, "name"), str);
ARRAY->Set(i, obj);
}
}
args.GetReturnValue().Set(ARRAY);
}
我从我的 Node 应用程序调用 C++ 方法:
var programs = ext.getPrograms();
for (var i = 0; i < programs.length; i++) {
console.log(programs[i][name]);
}
这主要是可行的,但是当程序名称包含非 ASCII 字符时,例如 Æ
、Ø
、Å
,next ARRAY 中的元素有一个乏味的名称。
与预期输出相比,节点代码段实际输出 (console.log
) 如下:
| ACTUAL | EXPECTED |
| --------- | ---------- |
| NRK SUPER | NRK SUPER |
| NRK VUPER | NRK VÆR |
| NRK P1 ER | NRK P1 |
似乎非 ASCII 字符导致下一个 wcstombs
提前退出,而不是复制后面的字符。
为什么会这样?有没有更好的方法从我的 wchar_t
创建一个 v8::String
?
注:
当 运行 在 Raspberry Pi 上时,我现在已经能够将这个问题隔离到 wcstombs
方法。以下代码:
#include <stdio.h>
#include <string>
#include <cstring>
#include <cstdlib>
char cbuff[600];
wchar_t buff[300] = L"ABCø123abc";
int main( int argc, const char* argv[] ) {
wcstombs( cbuff, buff, wcslen(buff) );
wprintf(L"wcslen of wchar_t array: %u - strlen of char array: %u\n", (char) wcslen(buff), strlen(cbuff));
}
当 运行 在 Mac 上时,输出
wcslen of wchar_t array: 10 - strlen of char array: 10
,
但是当 运行 在 Raspberry 上时,输出
wcslen of wchar_t array: 10 - strlen of char array: 3
- 也就是说,它只计算 ø
字符
这看起来类似于 this unanswered question。
我猜 wcstombs 中的最后一个参数是问题的原因。而不是尝试
wcstombs( cbuff, buff, wcslen(buff) );
尝试
memset(cbuff, 0, sizeof(cbuff));
wcstombs( cbuff, buff, sizeof(cbuff) );
问题出在 wcstombs( cbuff, buff, wcslen(buff) )
调用中,它会在遇到非 ASCII 字符时停止复制字符。 docs 表示 此函数的行为取决于所选 C 语言环境的 LC_CTYPE 类别。
因此将语言环境设置为 UTF-8 变体解决了问题:
setlocale(LC_CTYPE, "C.UTF-8");
完成此操作后,我现在可以这样创建 v8::String
:
wchar_t buff[300] = L"Something non-ASCII ÆØÅ here";
char cbuff[600];
wcstombs( cbuff, buff, wcslen(buff) );
Local<String> str = String::NewFromUtf8(isolate, (const char *) cbuff, v8::String::kNormalString, wcslen(buff));
WCHAR str[256];0
... // fill str array here
Local<String> v8str = String::NewFromTwoByte(isolate, (const uint16_t *) str);
注意 ::NewFromTwoByte
用法而不是 ::NewFromUtf8
和 (const uint16_t *)
转换。
::NewFromTwoByte
Allocates a new string from UTF-16 data.