如何检测终端中的unicode字符串宽度



我正在开发一个支持unicode的基于终端的程序。在某些情况下,我需要在打印字符串之前确定一个字符串将使用多少个终端列。不幸的是,有些字符有2列宽(中文等),但我发现这个答案表明,检测全宽字符的一个好方法是从ICU库调用ugetIntPropertyValue()。

现在我正在尝试解析UTF8字符串中的字符,并将它们传递给这个函数。我现在遇到的问题是,ugetIntPropertyValue()需要一个UTF-32代码点。

从utf8字符串中获取此值的最佳方法是什么?我目前正在尝试使用boost::locale(在我的程序中的其他地方使用)来实现这一点,但我很难获得干净的转换。我的来自boost::locale的UTF32字符串用一个零宽度字符来表示字节顺序。显然,我可以跳过字符串的前四个字节,但有更干净的方法吗?

这是我目前丑陋的解决方案:

inline size_t utf8PrintableSize(const std::string &str, std::locale loc)
{
namespace ba = boost::locale::boundary;
ba::ssegment_index map(ba::character, str.begin(), str.end(), loc);
size_t widthCount = 0;
for (ba::ssegment_index::iterator it = map.begin(); it != map.end(); ++it)
{
++widthCount;
std::string utf32Char = boost::locale::conv::from_utf(it->str(), std::string("utf-32"));
UChar32 utf32Codepoint = 0;
memcpy(&utf32Codepoint, utf32Char.c_str()+4, sizeof(UChar32));
int width = u_getIntPropertyValue(utf32Codepoint, UCHAR_EAST_ASIAN_WIDTH);
if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE))
{
++widthCount;
}
}
return widthCount;
}

@n.m是正确的:有一种简单的方法可以直接用ICS实现这一点。更新后的代码如下。我怀疑在这种情况下,我可能只需要使用UnicodeString,就可以绕过整个boost区域设置的使用。

inline size_t utf8PrintableSize(const std::string &str, std::locale loc)
{
namespace ba = boost::locale::boundary;
ba::ssegment_index map(ba::character, str.begin(), str.end(), loc);
size_t widthCount = 0;
for (ba::ssegment_index::iterator it = map.begin(); it != map.end(); ++it)
{
++widthCount;
//Note: Some unicode characters are 'full width' and consume more than one
// column on output.  We will increment widthCount one extra time for
// these characters to ensure that space is properly allocated
UnicodeString ucs = UnicodeString::fromUTF8(StringPiece(it->str()));
UChar32 codePoint = ucs.char32At(0);
int width = u_getIntPropertyValue(codePoint, UCHAR_EAST_ASIAN_WIDTH);
if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE))
{
++widthCount;
}
}
return widthCount;
}

UTF-32是单个字符的"代码点"的直接表示。因此,您所需要做的就是从UTF-8字符中提取这些字符,并将其提供给u_getIntPropertyValue

我取了你的代码,并将其修改为使用u8_to_u32_iterator,这似乎只是为了这个:

#include <boost/regex/pending/unicode_iterator.hpp>
inline size_t utf8PrintableSize(const std::string &str, std::locale loc)
{
size_t widthCount = 0;
for(boost::u8_to_u32_iterator<std::string::iterator> it(input.begin()), end(input.end()); it!=end; ++it)
{
++widthCount;
int width = u_getIntPropertyValue(*it, UCHAR_EAST_ASIAN_WIDTH);
if ((width == U_EA_FULLWIDTH) || (width == U_EA_WIDE))
{
++widthCount;
}
}
return widthCount;
}

最新更新