c# - 如何在 C# 中创建未转义的十六进制字节字符串

标签 c# c++ arrays pointers wrapper

我正在使用 C# 代码库中的 C++ API,但在文件路径中遇到西里尔字符问题。

我正在尝试调用包装的 C++ 函数,该函数应从文件加载对象。 C++ 函数签名如下所示:

GetModelFromFileCpp(ModelRefType * model, const char * file_path)

此函数包装在我的 C# 库中,如下所示:

[DllImport("CPPLibrary.dll", EntryPoint="GetModelFromFileCpp", CallingConvention=CallingConvention.Cdecl)]
public static extern ResultType GetModelFromFileCs(out IntPtr model, string file_path)

我无法访问 C++ 库来查看内部发生的情况。我应该补充一点,C++ 库的文档提到该函数期望文件路径是 UTF-8 编码的。

当我传入一个字符串来表示文件的绝对路径时,就会出现问题,并且该字符串包含西里尔字符。我也看到了日语字符的这个问题。例如,该字符串可能是“C:\Users\UserDirWithäöChar\App Data\Local\Temp”,其中 Windows 用户名包含这些字符。

用户名包含这些字符的事实很重要,因为我的代码正在生成一个临时文件,该文件放置在\AppData\Local\Temp 中,除非我在其中进行调试,否则该文件对于复制和放置在其他地方似乎并不友好管理模式。因此,似乎我被迫使用包含这些字符的路径。

我创建了以下脚本来测试字符串编码。

string path = @"C:\Users\UserDirWithäöChar\App Data\Local\Temp";
byte[] b = Encoding.UTF8.GetBytes(path);
string r = String.Empty;
foreach(byte bite in b)
{
   r += (@"\x" + String.Format("{0:X2}", bite));
}
var result = r.ToCharArray();
Console.WriteLine(result);

结果: \x43\x3A\x5C\x55\x73\x65\x72\x73\x5C\x55\x73\x65\x72\x44\x69\x72\x57\x69\x74\x68\xC3\xA4\xC3\xB6\x43\x68\x61\x72\x5C\x41\x70\x70\x20\x44\x61\x74\x61\x5C\x4C\x6F\x63\x61\x6C\x5C\x54\x65\x6D\x70

我发现,当我在调试器 session 期间(使用 Visual Studio 中的即时窗口)直接复制此输出并将其粘贴到包装器函数中时,C++ 库函数会给出正确的结果。

但是,如果我传入存储该值的变量,我会发现发生了序列化错误,并且无法读取(或找到)该文件。看来,当将此字符串设置为字符串变量时,字符将从这些十六进制表示形式转换为实际字符,其中包括转义的反斜杠: "\\x43\\x3A\\x5C\\x55\\x73\\x65\\x72\\x73\\x5C\\x55\\x73\\x65\\x72\\x44\\x69\\x72\\x57\\x69\\x74\\x68\\xC3\\xA4\xC3\xB6\x43\x68\x61\x72\x5C\x41\x70\x70\x20\x44\x61\x74\x61\x5C\\x4C\\x6F\\x63\\x61\\x6C\\x5C\\x54\\x65\\x6D\\x70"

我发现的一个有趣的事情是,第一个字符串的大小比最后一个字符串(带有转义反斜杠的字符串)的大小小 4 倍,但这两个字符串的计算结果都是 String 类型。

这两者之间有什么区别,是否可以使用第一个字符串的精确值设置变量(没有额外的反斜杠)?

编辑:这是我的完整实现

public static Model CreateModelFromFile(string path)
{
   byte[] bytes = Encoding.UTF8.GetBytes(path);

   string encodedPath = String.Empty;

   foreach(byte b in bytes)
   {
       encodedPath += (@"\x" + String.Format("{0:X2}", b));
   }

   IntPtr model_ref;

   if (ModelAPI.CreateFromFileWithStatus(out model_ref, 
       encodedPath)
      {
         return new Model(model_ref);
      {

   return null;
}

此外,我尝试在调试期间将 @ 添加到字符串文字的开头,但这不起作用。

该字符串在高于此级别的进程中传入 - 使用 System.IO.Path.GetTempFileName() 从文件系统检索。

最佳答案

您传递 UTF8 所做的根本不是 UTF8 应该如何表示的。这正是在 C# 代码中将其编写为文字字符串的方式。您在调试器中显示的只是实际字节的十六进制表示。

在实际的UTF8字符串中,每个字符占用一个字节,如果使用U+007F以上的字符则占用多个字节。

不幸的是,没有针对 UTF8 的自动编码,仅针对 ANSI 或 UTF16。

您需要将其作为空终止字节数组传递

[DllImport("CPPLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ResultType GetModelFromFileCpp(out IntPtr model, byte[] file_path)
public static Model CreateModelFromFile(string path)
{
    byte[] pathAsBytes = Encoding.UTF8.GetBytes(path + '\0');

    if (ModelAPI.CreateFromFileWithStatus(out var model_ref, pathAsBytes))
    {
        return new Model(model_ref);
    }

    return null;
}

关于c# - 如何在 C# 中创建未转义的十六进制字节字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73226969/

相关文章:

c# - 如果 CancellationToken 是一个 struct 并通过 Value 传递,它是如何更新的?

javascript - 将 JS 数组拆分为 N 个数组

c# - 无限数量的子弹

c# - 三次贝塞尔曲线问题

c++ - 在 C++ 中使用堆栈

c++ - 使用包含类的动态实例化后调用 C++ 重载运算符 [] 似乎不起作用

c++ - itoa 替换为 std::to_string 如何

c++ - 创建多个结构然后按元素排序

javascript - 迭代两个数组并执行 AND/OR 比较 = 返回 true

c# - C# 应用程序的最大开放端口数