使用sscanf将字符串转换为GUID

7
我正在尝试使用sscanf将字符串转换为GUID:
GUID guid;
sscanf( "11111111-2222-3333-4455-667788995511", "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
        &guid.Data1, &guid.Data2, &guid.Data3,
        &guid.Data4[0], &guid.Data4[1], &guid.Data4[2],
        &guid.Data4[3], &guid.Data4[4], &guid.Data4[5],
        &guid.Data4[6], &guid.Data4[7]);

然而,在运行时,它失败并显示“错误:命令失败”。为什么?如何修复?

我不想使用 /clr 进行编译,因此无法使用 System

5个回答

9

我认为你正在破坏堆栈。X类型说明符要求指向至少4个字节的int的指针,因此从&guid.Data [4]参数开始,您已经搞砸了。为sscanf提供足够的空间,那么你就没问题了。最终代码如下:

    GUID guid;

    unsigned long p0;
    int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;

    int err = sscanf_s(s.c_str(), "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
        &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10);

    guid.Data1 = p0;
    guid.Data2 = p1;
    guid.Data3 = p2;
    guid.Data4[0] = p3;
    guid.Data4[1] = p4;
    guid.Data4[2] = p5;
    guid.Data4[3] = p6;
    guid.Data4[4] = p7;
    guid.Data4[5] = p8;
    guid.Data4[6] = p9;
    guid.Data4[7] = p10;

5
"Error: Command failed" 来自哪里?这不是标准的错误信息...
你可以使用 UuidFromString 函数在本地 C++ 中实现它。

我正在使用haXe编写针对c++的代码,所以错误可能来自于haXe测试程序...但是问题中的代码是纯c++。当我使用UuidFromString("11111111-2222-3333-4455-667788995511",&guid);时,它会显示error C2664: 'UuidFromStringA' : cannot convert parameter 1 fr om 'const char *' to 'RPC_CSTR'... - Andy Li
@Billy:没错,但他说他不想使用“/clr”,这也是仅限于Windows的,所以我认为这是一个可以做出的合理假设。 - Dean Harding
@Andy:我认为一个简单的转换会起作用: const char *str = "..."; UuidFromString((RPC_CSTR) str, &guid); - Dean Harding

3

工作时不破坏堆栈:

GUID guid;

sscanf_s(AliasName.c_str(), "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
        &guid.Data1, &guid.Data2, &guid.Data3,
        &guid.Data4[0], &guid.Data4[1], &guid.Data4[2],
        &guid.Data4[3], &guid.Data4[4], &guid.Data4[5],
        &guid.Data4[6], &guid.Data4[7]);

2
自从 C++11 和 C99 推出以来,现在可以使用 参数大小指示符 ,例如 hh(表示单字节数据),将 GUID 字符串直接解析到 GUID 结构中。然而,正确且可移植的方法不依赖于平台的 longintshort 大小,而是使用 {{link2:<inttypes.h>}} 中提供的宏(或 C++11 中的 <cinttypes>):
#include <inttypes.h>

#define G32 "%8" SCNx32
#define G16 "%4" SCNx16
#define G8  "%2" SCNx8

bool to_guid(const char* str, GUID* guid) {
  int nchars = -1;
  int nfields =
    sscanf(str, "{" G32 "-" G16 "-" G16 "-" G8 G8 "-" G8 G8 G8 G8 G8 G8 "}%n",
           &guid->Data1, &guid->Data2, &guid->Data3,
           &guid->Data4[0], &guid->Data4[1], &guid->Data4[2], &guid->Data4[3],
           &guid->Data4[4], &guid->Data4[5], &guid->Data4[6], &guid->Data4[7],
           &nchars);
  return nfields == 11 && nchars == 38;
}

#undef G8
#undef G16
#undef G32

在 `` 中的宏可能会因不同编译器和系统位数而有所不同;仅以我的系统为例,它们在 `` 中的定义如下:
#define SCNx8        "hhx"
#define SCNx16       "hx"
#define SCNx32       "x"
#define SCNx64       "llx"

该段文字是关于解析字符串的说明。在解析一个GUID字符串时,使用%n可以返回已解析的字符串长度,如果GUID字符串缺少结尾的括号},则%n不会被执行,此时nchars的初始值将是-1。而如果GUID字符串的长度符合要求(38个字符),%n将返回字符串的长度,否则可能会出现错误的情况。需要注意的是,对于sscanf函数的返回值来说,%n本身并不算作一个“field”(字段)。另外,该文还提到了GUID字符串中可以使用空格代替前导零的情况。
{  FACFFB-   C-4DF3-A06C-D4 1 A 2 B 3}

仍将解析为它是这样的。
{00FACFFB-000C-4DF3-A06C-D4010A020B03}

但是,仅使用一个sscanf,可能只能做到这一步。

0

如果你想避免依赖于RPCRT4.DLL,你也可以使用CLSIDFromString - CLSID只是一个GUID,因此它将在名称略微混淆的情况下工作。


担心它会在UUID格式不正确时在注册表中查找ProgID字符串(但是IIDFromString()则不需要花括号)。此外,只有宽字符版本可用。除非使用COM,否则我不会选择它(您将获得OLE2.DLL而不是RPCRT4的依赖项,这也不是一个公平的交换)。Raymond Chen有更多信息:https://blogs.msdn.microsoft.com/oldnewthing/20151015-00/?p=91351 - kkm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接