在 Rich Edit 控件中迭代 WCHAR

问题描述 投票:0回答:1

我正在使用 Rich Edit 控件,并且想要迭代其中的

WCHAR
值。

我写了这个例程:

int GetCharacterAtIndex(int pos) const
{
    TEXTRANGE tr{};
    tr.chrg.cpMin = pos;
    tr.chrg.cpMax = pos + 1;
    WCHAR buffer[2]{};
    tr.lpstrText = buffer;
    DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
    assert(charsRetrieved < DIM(buffer));
    if (charsRetrieved <= 0) return -1;
    return buffer[0];
}

只要

pos
处的字符不是 UTF-16 代理项对的一部分,此方法就非常有效。但是,如果它是代理对的一部分,则返回的值始终是低代理项。这非常烦人,因为 charsRetrieved 中返回的值仍然只是 1。
我修改了代码如下,以使用

EM_GETSELTEXT

获取高代理:

int GetCharacterAtIndex(int pos) const
{
    TEXTRANGE tr{};
    tr.chrg.cpMin = pos;
    tr.chrg.cpMax = pos + 1;
    WCHAR buffer[10]{}; // add some extra space
    tr.lpstrText = buffer;
    DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr);
    assert(charsRetrieved < DIM(buffer));
    if (charsRetrieved <= 0) return -1;
    int index = 0;
    if (buffer[0] >= 0xD800 && buffer[0] < 0xDC00)
    {
        SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETSEL, pos, pos + 1);
        DWORD surrogateStart{}, surrogateEnd{};
        SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSEL, (WPARAM)&surrogateStart, (LPARAM)&surrogateEnd);
        if (surrogateStart == surrogateEnd)
        {
            // this may be a WinAPI bug. When pos actually points to the low surrogate, you sometimes have to do this twice.
            SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_SETSEL, pos, pos + 1);
            SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSEL, (WPARAM)&surrogateStart, (LPARAM)&surrogateEnd);
        }
        LRESULT gotChars = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETSELTEXT, 0, (LPARAM)buffer);
        assert(gotChars < DIM(buffer));
        if (gotChars <= 0) return -1;
        index = pos - surrogateStart;
        assert(index < gotChars);
    }
    return buffer[index];
}

这可行,但它依赖于
EM_GETSELTEXT

EM_GETSELTEXT
的问题在于它需要对选择进行破坏。即使您禁止重绘,这也会导致视觉伪像。它可能会导致更混乱的代码来抑制选择更改通知(或不抑制),并且需要调用者在需要时保存和恢复选择。
最重要的是它非常慢并且会产生丑陋的视觉伪像。

我是否错过了一种更优雅的方式来完成此任务?

EM_GETTEXTRANGE

快速、安静且独立。我真的不想使用

EM_GETSELTEXT
    

winapi utf-16 richedit
1个回答
0
投票
std::wstring

形式返回完整的 Unicode 字符。然后,如果需要,调用者可以在返回值上使用

size
方法来递增迭代器。
此外,我还纠正了问题中的术语。高代理优先。 (在我的问题中,我把术语搞反了。)

std::wstring GetCharacterAtIndex(int pos) const { TEXTRANGE tr{}; tr.chrg.cpMin = pos; tr.chrg.cpMax = pos + 1; WCHAR buffer[10]{}; // extra space tr.lpstrText = buffer; DWORD charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr); assert(charsRetrieved < DIM(buffer)); if (charsRetrieved <= 0) return L""; if (buffer[0] >= 0xD800 && buffer[0] < 0xDC00) // if it is the high surrogate of UTF-16 surrogate pair { // EM_GETTEXTRANGE always returns the high surrogate, even if pos points to a low surrogate. buffer[0] = 0; tr.chrg.cpMin = pos; tr.chrg.cpMax = pos + 2; charsRetrieved = SendDlgItemMessageW(GetWindowHandle(), GetControlID(), EM_GETTEXTRANGE, 0, (LPARAM)&tr); assert(charsRetrieved < DIM(buffer)); if (charsRetrieved <= 0) return L""; } return buffer; }

© www.soinside.com 2019 - 2024. All rights reserved.