Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

strprintfを改良してナロー文字も扱えるようにする #1810

Merged
merged 5 commits into from
Feb 27, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 147 additions & 28 deletions sakura_core/util/string_ex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "string_ex.h"

#include <stdarg.h>
#include <array>

#include "charset/charcode.h"
#include "charset/codechecker.h"
Expand Down Expand Up @@ -240,81 +241,199 @@ const char* stristr_j( const char* s1, const char* s2 )

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
事前に確保したバッファに結果を書き込む高速バージョン
@param[in, out] strOut フォーマットされたテキストを受け取る変数
@param[in] pszFormat フォーマット文字列
@param[in] argList 引数リスト
@returns フォーマットされた文字列
@returns 出力された文字数。NUL終端を含まない。
@retval >= 0 正常終了
@retval < 0 異常終了
*/
std::wstring vstrprintf(const WCHAR* pszFormat, va_list& argList)
int vstrprintf(std::wstring& strOut, const WCHAR* pszFormat, va_list& argList)
{
// _vscwprintf() はフォーマットに必要な文字数を返す。
const int cchOut = ::_vscwprintf(pszFormat, argList);
if (cchOut == 0) {
// 出力文字数が0なら後続処理は要らない。
return std::wstring();
// 出力文字数が0なら後続処理は要らない。
if (!cchOut) {
return 0;
berryzplus marked this conversation as resolved.
Show resolved Hide resolved
}

// 必要なバッファを確保する
if (const size_t required = cchOut + 1;
strOut.capacity() <= required)
{
strOut.resize(required, L'0');
}

// 必要なバッファを確保してフォーマットする
std::wstring strOut(cchOut + 1, L'0');
// フォーマットする
::vswprintf_s(strOut.data(), strOut.capacity(), pszFormat, argList);
strOut.resize(cchOut);
return strOut;

// NUL終端する
if (strOut.empty() && cchOut < 8) {
std::array<wchar_t, 8> buf;
::wcsncpy_s(buf.data(), buf.size(), strOut.data(), cchOut);
strOut.assign(buf.data(), cchOut);
}
else {
strOut.assign(strOut.data(), cchOut);
}

return cchOut;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
事前に確保したバッファに結果を書き込む高速バージョン
@param[in, out] strOut フォーマットされたテキストを受け取る変数
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@returns フォーマットされた文字列
@param[in] argList 引数リスト
@returns 出力された文字数。NUL終端を含まない。
@retval >= 0 正常終了
@retval < 0 異常終了
*/
std::wstring strprintf(const WCHAR* pszFormat, ...)
int vstrprintf(std::string& strOut, const CHAR* pszFormat, va_list& argList)
{
va_list argList;
va_start(argList, pszFormat);
// _vscwprintf() はフォーマットに必要な文字数を返す。
const int cchOut = ::_vscprintf(pszFormat, argList);
// 出力文字数が0なら後続処理は要らない。
if (!cchOut) {
return 0;
berryzplus marked this conversation as resolved.
Show resolved Hide resolved
}

const auto strRet = vstrprintf(pszFormat, argList);
// 必要なバッファを確保する
if (const size_t required = cchOut + 1;
strOut.capacity() <= required)
{
strOut.resize(required, L'0');
}

va_end(argList);
// フォーマットする
::vsprintf_s(strOut.data(), strOut.capacity(), pszFormat, argList);

// NUL終端する
if (strOut.empty() && cchOut < 16) {
std::array<char, 16> buf;
::strncpy_s(buf.data(), buf.size(), strOut.data(), cchOut);
strOut.assign(buf.data(), cchOut);
}
else {
strOut.assign(strOut.data(), cchOut);
}

return strRet;

return cchOut;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
@param[out] strOut フォーマットされたテキストを受け取る変数
事前に確保したバッファに結果を書き込む高速バージョン
@param[in, out] strOut フォーマットされたテキストを受け取る変数
@param[in] pszFormat フォーマット文字列
@param[in] argList 引数リスト
@returns 出力された文字数。NUL終端を含まない。
@retval >= 0 正常終了
@retval < 0 異常終了
*/
int vstrprintf( std::wstring& strOut, const WCHAR* pszFormat, va_list& argList )
int strprintf(std::wstring& strOut, const WCHAR* pszFormat, ...)
{
// オーバーロードバージョンを呼び出す
strOut = vstrprintf(pszFormat, argList);
return static_cast<int>(strOut.length());
va_list argList;
va_start(argList, pszFormat);

const auto nRet = vstrprintf(strOut, pszFormat, argList);

va_end(argList);

return nRet;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
@param[out] strOut フォーマットされたテキストを受け取る変数
事前に確保したバッファに結果を書き込む高速バージョン
@param[in, out] strOut フォーマットされたテキストを受け取る変数
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@param[in] argList 引数リスト
@returns 出力された文字数。NUL終端を含まない。
@retval >= 0 正常終了
@retval < 0 異常終了
*/
int strprintf( std::wstring& strOut, const WCHAR* pszFormat, ... )
int strprintf(std::string& strOut, const CHAR* pszFormat, ...)
{
va_list argList;
va_start( argList, pszFormat );
va_start(argList, pszFormat);

const int nRet = vstrprintf( strOut, pszFormat, argList );
const auto nRet = vstrprintf(strOut, pszFormat, argList);

va_end( argList );
va_end(argList);

return nRet;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
動的にバッファを確保する簡易バージョン
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@returns フォーマットされた文字列
*/
std::wstring vstrprintf(const WCHAR* pszFormat, va_list& argList)
{
std::wstring strOut;
vstrprintf(strOut, pszFormat, argList);
sanomari marked this conversation as resolved.
Show resolved Hide resolved
return strOut;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
動的にバッファを確保する簡易バージョン
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@returns フォーマットされた文字列
*/
std::string vstrprintf(const CHAR* pszFormat, va_list& argList)
{
std::string strOut;
vstrprintf(strOut, pszFormat, argList);
return strOut;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
動的にバッファを確保する簡易バージョン
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@returns フォーマットされた文字列
*/
std::wstring strprintf(const WCHAR* pszFormat, ...)
{
va_list argList;
va_start(argList, pszFormat);

const auto strOut = vstrprintf(pszFormat, argList);

va_end(argList);

return strOut;
}

/*!
@brief C-Styleのフォーマット文字列を使ってデータを文字列化する。
動的にバッファを確保する簡易バージョン
@param[in] pszFormat フォーマット文字列
@param[in] ... 引数リスト
@returns フォーマットされた文字列
*/
std::string strprintf(const CHAR* pszFormat, ...)
{
va_list argList;
va_start(argList, pszFormat);

const auto strOut = vstrprintf(pszFormat, argList);

va_end(argList);

return strOut;
}

// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
// 文字コード変換 //
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
Expand Down
8 changes: 6 additions & 2 deletions sakura_core/util/string_ex.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,10 +205,14 @@ inline int auto_vsprintf_s(WCHAR* buf, size_t nBufCount, const WCHAR* format, va
#define auto_sprintf_s(buf, nBufCount, format, ...) ::_sntprintf_s((buf), nBufCount, _TRUNCATE, (format), __VA_ARGS__)
#define auto_snprintf_s(buf, nBufCount, format, ...) ::_sntprintf_s((buf), nBufCount, _TRUNCATE, (format), __VA_ARGS__)

int vstrprintf(std::wstring& strOut, const WCHAR* pszFormat, va_list& argList);
int vstrprintf(std::string& strOut, const CHAR* pszFormat, va_list& argList);
int strprintf(std::wstring& strOut, const WCHAR* pszFormat, ...);
int strprintf(std::string& strOut, const CHAR* pszFormat, ...);
std::wstring vstrprintf(const WCHAR* pszFormat, va_list& argList);
std::string vstrprintf(const CHAR* pszFormat, va_list& argList);
std::wstring strprintf(const WCHAR* pszFormat, ...);
int vstrprintf( std::wstring& strOut, const WCHAR* pszFormat, va_list& argList );
int strprintf( std::wstring& strOut, const WCHAR* pszFormat, ... );
std::string strprintf(const CHAR* pszFormat, ...);

// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
// 文字コード変換 //
Expand Down
107 changes: 105 additions & 2 deletions tests/unittests/test-string_ex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,23 @@ TEST(string_ex, strprintf)
/*!
@brief 独自定義のフォーマット関数(C-Style風)。
*/
TEST(string_ex, strprintfOutputToArg)
TEST(string_ex, strprintfWOutputToArg)
{
std::wstring text;
std::wstring text(1024, L'\0');
strprintf(text, L"%s-%d", L"test", 101);
ASSERT_STREQ(L"test-101", text.c_str());
}

/*!
@brief 独自定義のフォーマット関数(C-Style風)。
*/
TEST(string_ex, strprintfAOutputToArg)
{
std::string text(1024, '\0');
strprintf(text, "%ls-of-active-codepage-%d", L"test", 101);
ASSERT_STREQ("test-of-active-codepage-101", text.c_str());
}

/*!
@brief 独自定義のフォーマット関数(空文字出力テスト)。
*/
Expand All @@ -135,6 +145,99 @@ TEST(string_ex, strprintfEmpty)
ASSERT_TRUE(text.empty());
}

/*!
@brief 独自定義のフォーマット関数(C-Style風)。

バッファが極端に小さい場合の確認。
wtd::wstringのスモールバッファは7文字分。
*/
TEST(string_ex, strprintfW_small_output)
{
std::wstring text = strprintf(L"");
EXPECT_STREQ(L"", text.c_str());

text = strprintf(L"%d", 1);
EXPECT_STREQ(L"1", text.c_str());

text = strprintf(L"%d", 12);
EXPECT_STREQ(L"12", text.c_str());

text = strprintf(L"%d", 123);
EXPECT_STREQ(L"123", text.c_str());

text = strprintf(L"%d", 1234);
EXPECT_STREQ(L"1234", text.c_str());

text = strprintf(L"%d", 12345);
EXPECT_STREQ(L"12345", text.c_str());

text = strprintf(L"%d", 123456);
EXPECT_STREQ(L"123456", text.c_str());

text = strprintf(L"%d", 1234567);
EXPECT_STREQ(L"1234567", text.c_str());
}

/*!
@brief 独自定義のフォーマット関数(C-Style風)。

バッファが極端に小さい場合の確認
wtd::stringのスモールバッファは15文字分。
*/
TEST(string_ex, strprintfA_small_output)
{
std::string text = strprintf("");
EXPECT_STREQ("", text.c_str());

text = strprintf("%d", 1);
EXPECT_STREQ("1", text.c_str());

text = strprintf("%d", 12);
EXPECT_STREQ("12", text.c_str());

text = strprintf("%d", 123);
EXPECT_STREQ("123", text.c_str());

text = strprintf("%d", 1234);
EXPECT_STREQ("1234", text.c_str());

text = strprintf("%d", 12345);
EXPECT_STREQ("12345", text.c_str());

text = strprintf("%d", 123456);
EXPECT_STREQ("123456", text.c_str());

text = strprintf("%d", 1234567);
EXPECT_STREQ("1234567", text.c_str());

text = strprintf("%d", 12345678);
EXPECT_STREQ("12345678", text.c_str());

text = strprintf("%d", 123456789);
EXPECT_STREQ("123456789", text.c_str());

text = strprintf("%d", 1234567890);
EXPECT_STREQ("1234567890", text.c_str());

text = strprintf("1234567890%d", 1);
EXPECT_STREQ("12345678901", text.c_str());

text = strprintf("1234567890%d", 12);
EXPECT_STREQ("123456789012", text.c_str());

text = strprintf("1234567890%d", 123);
EXPECT_STREQ("1234567890123", text.c_str());

text = strprintf("1234567890%d", 1234);
EXPECT_STREQ("12345678901234", text.c_str());

text = strprintf("1234567890%d", 12345);
EXPECT_STREQ("123456789012345", text.c_str());

text = strprintf("1234567890%d", 123456);
EXPECT_STREQ("1234567890123456", text.c_str());
}

/*!
@brief 独自定義の文字列比較関数。
*/
Expand Down