Добавил возможность экранировать не-ASCII символы при кодировании в UTF-8
This commit is contained in:
parent
9afb0b9976
commit
0d6e6e7333
@ -48,7 +48,7 @@ public:
|
|||||||
operator std::string() const;
|
operator std::string() const;
|
||||||
operator std::wstring() 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 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;
|
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 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 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 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);
|
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 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 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_symbol_size(const char32_t symbol, bool needEscape);
|
||||||
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 ucs4_get_utf16_symbol_size(const char32_t symbol);
|
std::size_t ucs4_get_utf16_symbol_size(const char32_t symbol);
|
||||||
std::size_t ucs4_get_utf16_str_bytes(const char32_t* str);
|
std::size_t ucs4_get_utf16_str_bytes(const char32_t* str);
|
||||||
|
|
||||||
|
|||||||
@ -151,24 +151,22 @@ ustring::operator std::wstring() const
|
|||||||
/**
|
/**
|
||||||
* Возвращает указатель на временный буфер с UTF-8 строкой
|
* Возвращает указатель на временный буфер с UTF-8 строкой
|
||||||
* @param addBom Указывает, добавлять ли BOM в возвращаемую строку
|
* @param addBom Указывает, добавлять ли BOM в возвращаемую строку
|
||||||
|
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||||
* @return Указатель на буфер со строкой, закодированной в UTF-8
|
* @return Указатель на буфер со строкой, закодированной в UTF-8
|
||||||
* @detailed Этот буфер является временным и будет высвобожден при любом изменении строки
|
* @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, needEscape);
|
||||||
std::size_t bomSize = (addBom ? sizeof(g_utf8_bom) : 0);
|
_pUtf8 = new char[bomSize + len + 1];
|
||||||
std::size_t len = ucs4_get_utf8_str_bytes(_pData);
|
ucs4_to_utf8(_pData, _pUtf8 + bomSize, length(), needEscape);
|
||||||
_pUtf8 = new char[bomSize + len + 1];
|
_pUtf8[len + bomSize] = char(0);
|
||||||
ucs4_to_utf8(_pData, _pUtf8 + bomSize, length());
|
|
||||||
_pUtf8[len + bomSize] = char(0);
|
|
||||||
|
|
||||||
if(addBom)
|
if(addBom)
|
||||||
{
|
{
|
||||||
memcpy(_pUtf8, g_utf8_bom, bomSize);
|
memcpy(_pUtf8, g_utf8_bom, bomSize);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return _pUtf8;
|
return _pUtf8;
|
||||||
}
|
}
|
||||||
@ -180,19 +178,16 @@ 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
|
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;
|
||||||
std::size_t bomSize = (addBom ? sizeof(g_utf16le_bom)/2 : 0);
|
_pUtf16 = new char16_t[bomSize + len + 1];
|
||||||
std::size_t len = ucs4_get_utf16_str_bytes(_pData)/2;
|
ucs4_to_utf16(_pData, _pUtf16 + bomSize, length(), byteOrder);
|
||||||
_pUtf16 = new char16_t[bomSize + len + 1];
|
_pUtf16[len + bomSize] = char16_t(0);
|
||||||
ucs4_to_utf16(_pData, _pUtf16 + bomSize, length(), byteOrder);
|
|
||||||
_pUtf16[len + bomSize] = char16_t(0);
|
|
||||||
|
|
||||||
if(addBom)
|
if(addBom)
|
||||||
{
|
{
|
||||||
memcpy(_pUtf16, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf16le_bom : g_utf16be_bom), sizeof(g_utf16le_bom));
|
memcpy(_pUtf16, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf16le_bom : g_utf16be_bom), sizeof(g_utf16le_bom));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return _pUtf16;
|
return _pUtf16;
|
||||||
}
|
}
|
||||||
@ -204,18 +199,15 @@ 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
|
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];
|
||||||
std::size_t bomSize = (addBom ? sizeof(g_utf32le_bom)/sizeof(char32_t) : 0);
|
utf32_strcpy_with_convert_byteorder(_pData, _pUtf32 + bomSize, _len, byteOrder);
|
||||||
_pUtf32 = new char32_t[bomSize + _len + 1];
|
_pUtf32[_len + bomSize] = char16_t(0);
|
||||||
utf32_strcpy_with_convert_byteorder(_pData, _pUtf32 + bomSize, _len, byteOrder);
|
|
||||||
_pUtf32[_len + bomSize] = char16_t(0);
|
|
||||||
|
|
||||||
if(addBom)
|
if(addBom)
|
||||||
{
|
{
|
||||||
memcpy(_pUtf32, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf32le_bom : g_utf32be_bom), sizeof(g_utf32le_bom));
|
memcpy(_pUtf32, (byteOrder == BYTE_ORDER_LITTLE_ENDIAN ? g_utf32le_bom : g_utf32be_bom), sizeof(g_utf32le_bom));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return _pUtf32;
|
return _pUtf32;
|
||||||
}
|
}
|
||||||
@ -491,7 +483,7 @@ std::size_t ustring::store_to_file( const ustring& fileName, encoding enc ) cons
|
|||||||
{
|
{
|
||||||
case ustring::UTF8:
|
case ustring::UTF8:
|
||||||
data = utf8_str();
|
data = utf8_str();
|
||||||
dataSize = ucs4_get_utf8_str_bytes(_pData);
|
dataSize = ucs4_get_utf8_str_bytes(_pData, false);
|
||||||
file.write(utf8Bom, sizeof(utf8Bom));
|
file.write(utf8Bom, sizeof(utf8Bom));
|
||||||
break;
|
break;
|
||||||
case ustring::UTF16LE:
|
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 src Указатель на буфер с UCS-4 строкой
|
||||||
* @param dst Указатель на буфер для UTF-8 строки
|
* @param dst Указатель на буфер для UTF-8 строки
|
||||||
* @param symbols Количество символов в строке
|
* @param symbols Количество символов в строке
|
||||||
|
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||||
* @throw bad_conversion Генерируется в случае ошибки кодирования (хотя бы один символ не входит
|
* @throw bad_conversion Генерируется в случае ошибки кодирования (хотя бы один символ не входит
|
||||||
* в набор символов юникода (UCS-4))
|
* в набор символов юникода (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;
|
std::size_t sBits, totalBytes;
|
||||||
uint32_t s;
|
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]);
|
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]);
|
s = static_cast<uint32_t>(src[i]);
|
||||||
|
|
||||||
if(totalBytes == 1)
|
if(totalBytes == 1)
|
||||||
@ -215,6 +216,26 @@ void ucs4_to_utf8(const char32_t* src, char* dst, std::size_t symbols)
|
|||||||
++dst;
|
++dst;
|
||||||
continue;
|
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;
|
std::size_t pos = 0, shiftLeft, shiftRight;
|
||||||
for(std::size_t n = totalBytes; n > 1; --n)
|
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
|
* Возвращает количество байт, которые займет строка, будучи кодированной в UTF-8
|
||||||
* @param str Исходная строка в UCS-4
|
* @param str Исходная строка в UCS-4
|
||||||
|
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||||
* @return Количество байт, необходимые для UTF-8 представления строки
|
* @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;
|
std::size_t bytes = 0;
|
||||||
|
|
||||||
while(*str)
|
while(*str)
|
||||||
{
|
{
|
||||||
bytes += ucs4_get_utf8_symbol_size(*str);
|
bytes += ucs4_get_utf8_symbol_size(*str, needEscape);
|
||||||
++str;
|
++str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,16 +345,29 @@ std::size_t utf8_first_byte_mask(std::size_t symbolSize)
|
|||||||
/**
|
/**
|
||||||
* Вычисляет, какой размер будет у символа, если кодировать его в UTF-8
|
* Вычисляет, какой размер будет у символа, если кодировать его в UTF-8
|
||||||
* @param symbol Исходный символ в UCS-4
|
* @param symbol Исходный символ в UCS-4
|
||||||
|
* @param needEscape Определяет, нужно ли экранировать не-ASCII символы
|
||||||
* @return Размер UTF-8 символа в байтах
|
* @return Размер UTF-8 символа в байтах
|
||||||
* @throw bad_conversion Генерируется если символ не входит в набор символов юникода (UCS-4)
|
* @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)
|
if(symbol > MAX_UNICODE_CHARACTER_CODE)
|
||||||
{
|
{
|
||||||
throw bad_conversion("error converting UCS-4 to UTF-8", symbol);
|
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 sBits = significant_bits(symbol);
|
||||||
std::size_t sixBitBytes = sBits / 6;
|
std::size_t sixBitBytes = sBits / 6;
|
||||||
std::size_t restBits = 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, 0x3d, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xd6, 0xfb, 0x00, 0x00, 0x00, 0xb2,
|
||||||
0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00 };
|
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;
|
const std::size_t helloStrLen = 33;
|
||||||
|
|
||||||
@ -103,9 +106,9 @@ TEST(Convert, utf8_symbol_size)
|
|||||||
|
|
||||||
TEST(Convert, utf8_symbol_size_by_ucs4_symbol)
|
TEST(Convert, utf8_symbol_size_by_ucs4_symbol)
|
||||||
{
|
{
|
||||||
ASSERT_EQ(1, ucs4_get_utf8_symbol_size(*(const char32_t*)&helloStrUcs4[0]));
|
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]));
|
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]));
|
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)
|
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)
|
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);
|
std::size_t symbols = utf32_str_len((const char32_t*)helloStrUcs4);
|
||||||
char* data = new char[len];
|
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));
|
EXPECT_EQ(0, memcmp(helloStrUtf8, data, len));
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
}
|
||||||
@ -319,7 +322,7 @@ TEST(ConvertError, utf16_decode_error2)
|
|||||||
|
|
||||||
TEST(ConvertError, ucs4_wrong_symbol)
|
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);
|
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);
|
ustring str((const char32_t*)helloStrUcs4);
|
||||||
EXPECT_EQ(0, memcmp(helloStrUtf8, str.utf8_str(), sizeof(helloStrUtf8)));
|
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