c# - 在 C 中比较 Unicode 字符串返回与 C# 不同的值

标签 c# c unicode utf-8 string-comparison

所以我试图用 C 编写一个比较函数,它可以采用 UTF-8 编码的 Unicode 字符串并使用 Windows CompareStringEx()功能,我希望它能像 .NET 的 CultureInfo.CompareInfo.Compare() 一样工作.

现在,我用 C 编写的函数在某些时候有效,但并非在所有情况下都有效,我正试图找出原因。这是一个失败的案例(在 C# 中通过,而不是在 C 中):

CultureInfo cultureInfo = new CultureInfo("en-US");
CompareOptions compareOptions = CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth;

string stringA = "คนอ้วน ๆ";
string stringB = "はじめまして";
//Result is -1 which is expected
int result = cultureInfo.CompareInfo.Compare(stringA, stringB);

这是我用 C 编写的内容。请记住,这应该采用 UTF-8 编码的字符串并使用 Windows CompareStringEx() 函数,因此需要进行转换。

// Compare flags for the string comparison
#define COMPARE_STRING_FLAGS (NORM_IGNORECASE | NORM_IGNOREKANATYPE | NORM_IGNOREWIDTH)

int CompareStrings(int lenA, const void *strA, int lenB, const void *strB) 
{
    LCID ENGLISH_LCID = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
    int compareString = -1;

    // Get the size of the strings as UTF-18 encoded Unicode strings. 
    // Note: Passing 0 as the last parameter forces the MultiByteToWideChar function
    // to give us the required buffer size to convert the given string to utf-16s
    int strAWStrBufferSize = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strA, lenA, NULL, 0);
    int strBWStrBufferSize = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strB, lenB, NULL, 0);

    // Malloc the strings to store the converted UTF-16 values
    LPWSTR utf16StrA = (LPWSTR) GlobalAlloc(GMEM_FIXED, strAWStrBufferSize * sizeof(WCHAR));
    LPWSTR utf16StrB = (LPWSTR) GlobalAlloc(GMEM_FIXED, strBWStrBufferSize * sizeof(WCHAR));

    // Convert the UTF-8 strings (SQLite will pass them as UTF-8 to us) to standard  
    // windows WCHAR (UTF-16\UCS-2) encoding for Unicode so they can be used in the 
    // Windows CompareStringEx() function.
    if(strAWStrBufferSize != 0)
    {
        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strA, lenA, utf16StrA, strAWStrBufferSize);
    }
    if(strBWStrBufferSize != 0)
    {
        MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strB, lenB, utf16StrB, strBWStrBufferSize);
    }

    // Compare the strings using the windows compare function.
    // Note: We subtract 1 from the size since we don't want to include the null termination character
    if(NULL != utf16StrA && NULL != utf16StrB)
    {
        compareValue = CompareStringEx(L"en-US", COMPARE_STRING_FLAGS, utf16StrA, strAWStrBufferSize - 1, utf16StrB, strBWStrBufferSize - 1, NULL, NULL, 0);
    }

    // In the Windows CompareStringEx() function, 0 indicates an error, 1 indicates less than, 
    // 2 indicates equal to, 3 indicates greater than so subtract 2 to maintain C convention
    if(compareValue > 0)
    {
        compareValue -= 2;
    }

    return compareValue;
}

现在,如果我运行以下代码,我希望基于 .NET 实现(见上文)的结果为 -1,但我得到 1,表明字符串大于:

char strA[50] = "คนอ้วน ๆ";
char strB[50] = "はじめまして";

// Will be 1 when we expect it to be -1
int result = CompareStrings(strlen(strA), strA, strlen(strB), strB);

关于为什么我得到的结果不同有什么想法吗?我在两个实现中使用相同的 LCID/cultureInfo 和 compareOptions,据我所知,转换是成功的。

仅供引用:此函数将用作 SQLite 中的自定义排序规则。与问题无关,但万一有人想知道为什么函数签名是这样的。

更新:我还确定,当在 .NET 4 中运行相同的代码时,我会看到我在 native 代码中看到的行为。因此,现在 .NET 版本之间存在差异。关于这背后的原因,请参阅下面我的回答。

最佳答案

好吧,您的代码在这里执行了几个步骤 - 目前尚不清楚是否是比较步骤失败了。

作为第一步,我会在 .NET 代码和 C 代码中写出您在 utf16StrAutf16StrB 中获得的确切 UTF-16 代码单元stringAstringB。如果发现您在 C 代码中使用的输入数据存在问题,我一点也不会感到惊讶。

关于c# - 在 C 中比较 Unicode 字符串返回与 C# 不同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7339120/

相关文章:

javascript - 是否可以构造一个匹配 Grapheme Cluster Break=Extend 的\p JavaScript 正则表达式

unicode - 将 Unicode 字符打印到 PowerShell 提示符

bash - 如何使用 bash 工具搜索非 ASCII 字符?

c# - 为什么有人会在枚举声明中使用 << 运算符?

c# - SystemEvents.Time Change 显示同一时间甚至时区变化

c - C 中的第 16 行和第 21 行有问题。 (word[25], != '9' ) 是什么?这是我的代码 :

c++ - 函数和指针帮助 : Program builds but Crashes without any specific errors

c# - ASP.NET 数据库总是损坏

c# - Response.WriteFile 可以带有 URL 吗?

c - 调试宏奇怪的行为