Добавил возможность экранировать не-ASCII символы при кодировании в UTF-8
This commit is contained in:
parent
9afb0b9976
commit
0d6e6e7333
@ -48,7 +48,7 @@ public:
|
||||
operator std::string() const;
|
||||
operator std::wstring() const;
|
||||
|
||||
const char* utf8_str(bool addBom = false) const;
|
||||
const char* utf8_str(bool addBom = false, bool needEscape = false) const;
|
||||
const char16_t* utf16_str(bool addBom = false, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN) const;
|
||||
const char32_t* utf32_str(bool addBom = false, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN) const;
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ std::size_t significant_bits(uint32_t v);
|
||||
void utf32_strcpy_with_convert_byteorder(const char32_t* src, char32_t* dst, std::size_t num, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
|
||||
|
||||
void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols, bool needUnescape);
|
||||
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols);
|
||||
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols, bool needEscape);
|
||||
void utf16_to_ucs4(const char16_t* src, char32_t* dst, std::size_t symbols, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
|
||||
void ucs4_to_utf16(const char32_t* src, char16_t* dst, std::size_t symbols, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
|
||||
|
||||
@ -71,8 +71,8 @@ std::size_t utf16_str_len(const char16_t* str, byte_order byteOrder = BYTE_ORDER
|
||||
std::size_t utf16_get_symbol_size(const char16_t* str, byte_order byteOrder = BYTE_ORDER_LITTLE_ENDIAN);
|
||||
std::size_t utf32_str_len(const char32_t* str);
|
||||
|
||||
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol);
|
||||
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str);
|
||||
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol, bool needEscape);
|
||||
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str, bool needEscape);
|
||||
std::size_t ucs4_get_utf16_symbol_size(const char32_t symbol);
|
||||
std::size_t ucs4_get_utf16_str_bytes(const char32_t* str);
|
||||
|
||||
|
||||
@ -151,24 +151,22 @@ ustring::operator std::wstring() const
|
||||
/**
|
||||
* Возвращает указатель на временный буфер с UTF-8 строкой
|
||||
* @param addBom Указывает, добавлять ли BOM в возвращаемую строку
|
||||
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||
* @return Указатель на буфер со строкой, закодированной в UTF-8
|
||||
* @detailed Этот буфер является временным и будет высвобожден при любом изменении строки
|
||||
*/
|
||||
const char* ustring::utf8_str( bool addBom /*= false*/ ) const
|
||||
const char* ustring::utf8_str(bool addBom /*= false*/ , bool needEscape /*= false*/) const
|
||||
{
|
||||
if(!_pUtf8)
|
||||
{
|
||||
std::size_t bomSize = (addBom ? sizeof(g_utf8_bom) : 0);
|
||||
std::size_t len = ucs4_get_utf8_str_bytes(_pData);
|
||||
std::size_t len = ucs4_get_utf8_str_bytes(_pData, needEscape);
|
||||
_pUtf8 = new char[bomSize + len + 1];
|
||||
ucs4_to_utf8(_pData, _pUtf8 + bomSize, length());
|
||||
ucs4_to_utf8(_pData, _pUtf8 + bomSize, length(), needEscape);
|
||||
_pUtf8[len + bomSize] = char(0);
|
||||
|
||||
if(addBom)
|
||||
{
|
||||
memcpy(_pUtf8, g_utf8_bom, bomSize);
|
||||
}
|
||||
}
|
||||
|
||||
return _pUtf8;
|
||||
}
|
||||
@ -180,8 +178,6 @@ const char* ustring::utf8_str( bool addBom /*= false*/ ) const
|
||||
*/
|
||||
const char16_t* ustring::utf16_str( bool addBom /*= false*/, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) const
|
||||
{
|
||||
if(!_pUtf16)
|
||||
{
|
||||
std::size_t bomSize = (addBom ? sizeof(g_utf16le_bom)/2 : 0);
|
||||
std::size_t len = ucs4_get_utf16_str_bytes(_pData)/2;
|
||||
_pUtf16 = new char16_t[bomSize + len + 1];
|
||||
@ -192,7 +188,6 @@ const char16_t* ustring::utf16_str( bool addBom /*= false*/, byte_order byteOrde
|
||||
{
|
||||
memcpy(_pUtf16, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf16le_bom : g_utf16be_bom), sizeof(g_utf16le_bom));
|
||||
}
|
||||
}
|
||||
|
||||
return _pUtf16;
|
||||
}
|
||||
@ -204,8 +199,6 @@ const char16_t* ustring::utf16_str( bool addBom /*= false*/, byte_order byteOrde
|
||||
*/
|
||||
const char32_t* ustring::utf32_str( bool addBom /*= false*/, byte_order byteOrder /*= BYTE_ORDER_LITTLE_ENDIAN*/ ) const
|
||||
{
|
||||
if(!_pUtf32)
|
||||
{
|
||||
std::size_t bomSize = (addBom ? sizeof(g_utf32le_bom)/sizeof(char32_t) : 0);
|
||||
_pUtf32 = new char32_t[bomSize + _len + 1];
|
||||
utf32_strcpy_with_convert_byteorder(_pData, _pUtf32 + bomSize, _len, byteOrder);
|
||||
@ -215,7 +208,6 @@ const char32_t* ustring::utf32_str( bool addBom /*= false*/, byte_order byteOrde
|
||||
{
|
||||
memcpy(_pUtf32, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf32le_bom : g_utf32be_bom), sizeof(g_utf32le_bom));
|
||||
}
|
||||
}
|
||||
|
||||
return _pUtf32;
|
||||
}
|
||||
@ -491,7 +483,7 @@ std::size_t ustring::store_to_file( const ustring& fileName, encoding enc ) cons
|
||||
{
|
||||
case ustring::UTF8:
|
||||
data = utf8_str();
|
||||
dataSize = ucs4_get_utf8_str_bytes(_pData);
|
||||
dataSize = ucs4_get_utf8_str_bytes(_pData, false);
|
||||
file.write(utf8Bom, sizeof(utf8Bom));
|
||||
break;
|
||||
case ustring::UTF16LE:
|
||||
|
||||
45
src/utf.cpp
45
src/utf.cpp
@ -190,10 +190,11 @@ void utf8_to_ucs4(const char* src, char32_t* dst, std::size_t symbols, bool need
|
||||
* @param src Указатель на буфер с UCS-4 строкой
|
||||
* @param dst Указатель на буфер для UTF-8 строки
|
||||
* @param symbols Количество символов в строке
|
||||
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||
* @throw bad_conversion Генерируется в случае ошибки кодирования (хотя бы один символ не входит
|
||||
* в набор символов юникода (UCS-4))
|
||||
*/
|
||||
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
|
||||
void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols, bool needEscape)
|
||||
{
|
||||
std::size_t sBits, totalBytes;
|
||||
uint32_t s;
|
||||
@ -206,7 +207,7 @@ void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
|
||||
}
|
||||
|
||||
sBits = significant_bits(src[i]);
|
||||
totalBytes = ucs4_get_utf8_symbol_size(src[i]);
|
||||
totalBytes = ucs4_get_utf8_symbol_size(src[i], needEscape);
|
||||
s = static_cast<uint32_t>(src[i]);
|
||||
|
||||
if(totalBytes == 1)
|
||||
@ -215,6 +216,26 @@ void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
|
||||
++dst;
|
||||
continue;
|
||||
}
|
||||
else if(needEscape)
|
||||
{
|
||||
if(src[i] < 0xFFFF)
|
||||
{
|
||||
if(sprintf(dst, "\\u%04X", src[i]) < 0)
|
||||
{
|
||||
throw bad_conversion("error escaping character", src[i]);
|
||||
}
|
||||
dst += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(sprintf(dst, "\\U%08X", src[i]) < 0)
|
||||
{
|
||||
throw bad_conversion("error escaping character", src[i]);
|
||||
}
|
||||
dst += 10;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t pos = 0, shiftLeft, shiftRight;
|
||||
for(std::size_t n = totalBytes; n > 1; --n)
|
||||
@ -256,15 +277,16 @@ std::size_t utf8_str_len(const char* str, bool needUnescape)
|
||||
/**
|
||||
* Возвращает количество байт, которые займет строка, будучи кодированной в UTF-8
|
||||
* @param str Исходная строка в UCS-4
|
||||
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||
* @return Количество байт, необходимые для UTF-8 представления строки
|
||||
*/
|
||||
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str)
|
||||
std::size_t ucs4_get_utf8_str_bytes(const char32_t* str, bool needEscape)
|
||||
{
|
||||
std::size_t bytes = 0;
|
||||
|
||||
while(*str)
|
||||
{
|
||||
bytes += ucs4_get_utf8_symbol_size(*str);
|
||||
bytes += ucs4_get_utf8_symbol_size(*str, needEscape);
|
||||
++str;
|
||||
}
|
||||
|
||||
@ -323,16 +345,29 @@ std::size_t utf8_first_byte_mask(std::size_t symbolSize)
|
||||
/**
|
||||
* Вычисляет, какой размер будет у символа, если кодировать его в UTF-8
|
||||
* @param symbol Исходный символ в UCS-4
|
||||
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||
* @return Размер UTF-8 символа в байтах
|
||||
* @throw bad_conversion Генерируется если символ не входит в набор символов юникода (UCS-4)
|
||||
*/
|
||||
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol)
|
||||
std::size_t ucs4_get_utf8_symbol_size(const char32_t symbol, bool needEscape)
|
||||
{
|
||||
if(symbol > MAX_UNICODE_CHARACTER_CODE)
|
||||
{
|
||||
throw bad_conversion("error converting UCS-4 to UTF-8", symbol);
|
||||
}
|
||||
|
||||
if(needEscape && symbol > 0x80)
|
||||
{
|
||||
if(symbol < 0xFFFF)
|
||||
{
|
||||
return 6; // \uXXXX
|
||||
}
|
||||
else
|
||||
{
|
||||
return 10; // \UXXXXXXXX
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t sBits = significant_bits(symbol);
|
||||
std::size_t sixBitBytes = sBits / 6;
|
||||
std::size_t restBits = sBits % 6;
|
||||
|
||||
@ -53,6 +53,9 @@ const unsigned char helloStrUtf32be[] = { 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x
|
||||
0x00, 0x00, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xd6, 0xfb, 0x00, 0x00, 0x00, 0xb2,
|
||||
0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
// Escaped
|
||||
const char helloStrEscaped[] = "Hello World!\n\\u041F\\u0440\\u0438\\u0432\\u0435\\u0442, \\u041C\\u0438\\u0440!\n\\u2206 = \\U0001D6FB\\u00B2\n";
|
||||
|
||||
// Длина тестовой строки в символах
|
||||
const std::size_t helloStrLen = 33;
|
||||
|
||||
@ -103,9 +106,9 @@ TEST(Convert, utf8_symbol_size)
|
||||
|
||||
TEST(Convert, utf8_symbol_size_by_ucs4_symbol)
|
||||
{
|
||||
ASSERT_EQ(1, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[0]));
|
||||
ASSERT_EQ(2, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[52]));
|
||||
ASSERT_EQ(4, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[120]));
|
||||
ASSERT_EQ(1, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[0], false));
|
||||
ASSERT_EQ(2, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[52], false));
|
||||
ASSERT_EQ(4, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[120], false));
|
||||
}
|
||||
|
||||
//
|
||||
@ -123,7 +126,7 @@ TEST(Convert, utf8_length)
|
||||
|
||||
TEST(Convert, utf8_str_len_by_ucs4_str)
|
||||
{
|
||||
ASSERT_EQ(sizeof(helloStrUtf8) - 1, ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4));
|
||||
ASSERT_EQ(sizeof(helloStrUtf8) - 1, ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4, false));
|
||||
}
|
||||
|
||||
//
|
||||
@ -205,10 +208,10 @@ TEST(Convert, utf8_to_ucs4)
|
||||
//
|
||||
TEST(Convert, ucs4_to_utf8)
|
||||
{
|
||||
std::size_t len = ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4);
|
||||
std::size_t len = ucs4_get_utf8_str_bytes((const char32_t*)helloStrUcs4, false);
|
||||
std::size_t symbols = utf32_str_len((const char32_t*)helloStrUcs4);
|
||||
char* data = new char[len];
|
||||
ucs4_to_utf8((const char32_t*)helloStrUcs4, data, symbols);
|
||||
ucs4_to_utf8((const char32_t*)helloStrUcs4, data, symbols, false);
|
||||
EXPECT_EQ(0, memcmp(helloStrUtf8, data, len));
|
||||
delete[] data;
|
||||
}
|
||||
@ -319,7 +322,7 @@ TEST(ConvertError, utf16_decode_error2)
|
||||
|
||||
TEST(ConvertError, ucs4_wrong_symbol)
|
||||
{
|
||||
EXPECT_THROW(ucs4_get_utf8_symbol_size(*(const char32_t*)badStrUcs4), bad_conversion);
|
||||
EXPECT_THROW(ucs4_get_utf8_symbol_size(*(const char32_t*)badStrUcs4, false), bad_conversion);
|
||||
EXPECT_THROW(ucs4_get_utf16_symbol_size(*(const char32_t*)badStrUcs4), bad_conversion);
|
||||
}
|
||||
|
||||
@ -374,6 +377,7 @@ TEST(Ustring, utf8_cast_operator)
|
||||
{
|
||||
ustring str((const char32_t*)helloStrUcs4);
|
||||
EXPECT_EQ(0, memcmp(helloStrUtf8, str.utf8_str(), sizeof(helloStrUtf8)));
|
||||
EXPECT_EQ(0, memcmp(str.utf8_str(false, true), helloStrEscaped, sizeof(helloStrEscaped)));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Loading…
Reference in New Issue
Block a user