如何在 c++ 中比较 char(或 wchar_t)作为字符串的一部分

How compare char (or wchar_t) as part of string in c++

提问人:Michal Hadraba 提问时间:11/2/2023 最后编辑:Remy LebeauMichal Hadraba 更新时间:11/2/2023 访问量:91

问:

我有两个 C 风格的以空结尾(宽)字符串不是 std::wstring),我通过迭代器浏览它们,并想逐个字符比较它们(用于排序)。我不需要使用标准运算符(,,),因为我想通过本地规则(设置_locale_t)来比较它们。==><

我只找到了 ,它可以比较两个字符串,而不是字符。_wcsicmp_l(wchar_t*, wchar_t*, _locale_t)

当我将指向比较函数上的参数的指针传递时,就像字符串的一部分(字符串的迭代器)一样,该函数会像指向字符串的指针一样获取它,并且正在比较两个完整的字符串,而不仅仅是字符。wchar_t

我认为一个例子会更能说明问题。这是一个好方法,还是太难太慢?有没有办法更容易做到?

目标提供自然排序 - 字符串包括字符和数字。

inline bool compareChars(const wchar_t& lhs, const wchar_t& rhs)
{
    //I have to add terminate \0 here, in otherwise comparator compare string from lhs to end, not only chars
    wchar_t str_lhs[2]{ lhs, L'\0' }, str_rhs[2]{ rhs, L'\0' };
    _locale_t loc = _create_locale(LC_ALL, "cs-CZ.utf8");
    bool res = _wcsicoll_l(&str_lhs[0], &str_rhs[0], loc) < 0;
    _free_locale(loc);
    return res;
}

int main()
{
    const wchar_t* firstStr = L"Test", * secondStr = L"Test2";
    const wchar_t* it1 = firstStr;
    const wchar_t* it2 = secondStr;
    const wchar_t* itEnd1 = it1;
    while (*itEnd1 != '\0')itEnd1++;
    const wchar_t* itEnd2 = it2;
    while (*itEnd2 != '\0')itEnd2++;

    for (; it1 != itEnd1; ++it1) //Go througth first string
        for (; it2 != itEnd2; ++it2) //Go througth second string
        {
            int res = compareChars(*it1, *it2); //chars like a part of string
        }

    return 0;
}
C++ 字符串 本地化比较

评论

0赞 Ted Lyngmo 11/2/2023
你是宽字符串常规的空终止(宽)字符串吗?
2赞 Igor Tandetnik 11/2/2023
通常,您不能逐个字符进行比较,同时遵循区域设置规则。许多语言环境中的排序规则比这更棘手。例如,在西班牙语中,两个字母的组合排序就好像它是 和 之间的单个字母。在法语中,首先比较字符串而不考虑变音符号,然后通过从最后一个字符到第一个字符向后比较来打破联系。chcd
0赞 Igor Tandetnik 11/2/2023
练习的最终目标是什么?你为什么要这样做?目前,这听起来像是一个 XY 问题
1赞 paddy 11/2/2023
鉴于您想要包含对自然数进行排序的规则,您可以尝试将字符串划分为多个部分。例如,在每个字符串中,扫描第一个数字(如果有),然后将子字符串与这些数字进行比较。如果子字符串相等,则扫描到下一个非数字并应用数字比较。如果它们相等,请切换回词典比较。以这种方式处理整个字符串。这很麻烦,但应该并不难。从性能角度来看,我强烈建议您不要每次都创建/删除区域设置。做一次。
1赞 paddy 11/2/2023
请注意上述内容:您将需要 _strnicoll 变体,它们可以对子字符串进行操作。

答:

0赞 Michal Hadraba 11/2/2023 #1

结果在这里:

#include <locale>
#include <string>

using namespace std;

namespace MajCompare {

struct MajLoc
{
private:
    _locale_t m_loc;
public:
    MajLoc(){ m_loc = _create_locale(LC_COLLATE, "cs-CZ.utf8"); }
    ~MajLoc() { _free_locale(m_loc);}
    _locale_t& GetLocale() { return m_loc; }
};
static MajLoc s_cLoc;

//Vrací -1, 0 1
int CompareNatural(const wchar_t* lhsBegin, const wchar_t* lhsEnd
    , const wchar_t* rhsBegin, const wchar_t* rhsEnd)
{

    wstring wsPom1, wsPom2;
    const wchar_t* itCurLhs = lhsBegin, *itCurRhs = rhsBegin;
    while (!iswdigit(*itCurLhs) && !iswdigit(*itCurRhs) && itCurLhs != lhsEnd && itCurRhs != rhsEnd)
    {
        wsPom1.push_back(*itCurLhs);
        wsPom2.push_back(*itCurRhs);
        if (*itCurLhs != *itCurRhs)
            return _wcsicoll_l(lhsBegin, rhsBegin, s_cLoc.GetLocale());

        itCurLhs++, itCurRhs++;
    }
    if (itCurLhs == lhsEnd || itCurRhs == rhsEnd || iswdigit(*itCurLhs) != iswdigit(*itCurRhs)) //jeden je delší nebo kombinace čísla a písmena, porovnáme původní řetězce bez ohledu na čísla porovnáme standardně
        return _wcsicoll_l(lhsBegin, rhsBegin, s_cLoc.GetLocale());
    else //oba řetězce mají čísla a nejsou na konci
    {
        wstring wsNum1, wsNum2;
        while (iswdigit(*itCurLhs))
            wsNum1.push_back(*itCurLhs++);
        while (iswdigit(*itCurRhs))
            wsNum2.push_back(*itCurRhs++);
        int iNum1 = _wtoi(wsNum1.data());
        int iNum2 = _wtoi(wsNum2.data());
        if (iNum1 != iNum2)
            return iNum1 > iNum2 ? 1 : -1;
        return CompareNatural(itCurLhs, lhsEnd, itCurRhs, rhsEnd);
    }
}

int CompareNatural(const wchar_t* lsFirst, const wchar_t* lsSecond)
{
    const wchar_t* it1 = lsFirst;
    while (*it1 != '\0')it1++;
    const wchar_t* it2 = lsSecond;
    while (*it2 != '\0')it2++;
    return CompareNatural(lsFirst, it1, lsSecond, it2);
}
};