Godot 4의 주요 문자열 유형은 String으로 비교적 잘 설계되었지만 서식이 많이 고려되지 않는 문제가 있습니다.
String에 포맷 함수가 있는데 이 함수는 매개변수가 2개뿐인데 어떻게 사용하나요?
String String::format(const Variant &values, String placeholder) const {
String new_string = String(this->ptr());
if (values.get_type() == Variant::ARRAY) {
Array values_arr = values;
for (int i = 0; i < values_arr.size(); i++) {
String i_as_str = String::num_int64(i);
if (values_arr[i].get_type() == Variant::ARRAY) { //Array in Array structure [["name","RobotGuy"],[0,"godot"],["strength",9000.91]]
Array value_arr = values_arr[i];
if (value_arr.size() == 2) {
Variant v_key = value_arr[0];
String key = v_key;
Variant v_val = value_arr[1];
String val = v_val;
new_string = new_string.replace(placeholder.replace("_", key), val);
} else {
ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data());
}
} else { //Array structure ["RobotGuy","Logis","rookie"]
Variant v_val = values_arr[i];
String val = v_val;
if (placeholder.find("_") > -1) {
new_string = new_string.replace(placeholder.replace("_", i_as_str), val);
} else {
new_string = new_string.replace_first(placeholder, val);
}
}
}
} else if (values.get_type() == Variant::DICTIONARY) {
Dictionary d = values;
List<Variant> keys;
d.get_key_list(&keys);
for (const Variant &key : keys) {
new_string = new_string.replace(placeholder.replace("_", key), d[key]);
}
} else {
ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data());
}
return new_string;
}
이 효과가 있는 사용 예 찾기
나는 첫눈에 깜짝 놀랐다. %s %d... 이전에 사용했던 형식은 어디에 있습니까? 사용하기 편합니다.
너 스스로해라
template <typename... Args>
static std::string str_format(const std::string &format, Args... args) {
auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;
std::unique_ptr<char[]> buf(new (std::nothrow) char[size_buf]);
if (!buf)
return std::string("");
std::snprintf(buf.get(), size_buf, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size_buf - 1);
}
template <typename... Args>
static String str_format(const std::u32string &format, Args... args) {
auto size_buf = std::snprintf(nullptr, 0, TDrString::Convert_u32String_stdString(format).c_str(), args...) + 1;
std::unique_ptr<char[]> buf(new (std::nothrow) char[size_buf]);
if (!buf)
return String(U"");
std::string strFormat = TDrString::Convert_u32String_stdString(format);
std::snprintf(buf.get(), size_buf, strFormat.c_str(), args...);
std::string str(buf.get(), buf.get() + size_buf - 1);
return String(str);
}
std::string 및 String 두 가지 서식 지정 효과를 제공합니다. 그런 다음 다음을 사용하십시오.
cofs << U"ERROR" << str_format(U"函数 [%s] 调用失败:参数个数不匹配,形参 [%d] 个,实参 [%d]个", drFunCall.GetHint().c_str(), it->arguments.size(), drFunCall.arguments.size());
그런데 인터넷에 돌려보니 fmt 라이브러리가 좋은 평가를 받고 있습니다. GitHub 에서 직접 가져오기 - fmtlib/fmt: 최신 서식 지정 라이브러리를 소스 코드에 추가하여 사용할 수 있습니다.
다만 fmt의 용도는 {}인데 좀 신선하고 C++ 20과도 호환이 될 것 같으니 우선 사용해보세요
fmt::format("[{}.Read] > 解析数据{}", prefixType, hint.utf8().ptr());
중국어가 직접 지원됩니다.
이 과정에서 작은 문제를 발견했습니다. std::string은 직접 String으로 변환할 수 없고 String 클래스는 많은 생성자를 제공하지만 std::string이 없습니다.
String(const char *p_str);
String(const wchar_t *p_str);
String(const char32_t *p_str);
String(const char *p_str, int p_clip_to_len);
String(const wchar_t *p_str, int p_clip_to_len);
String(const char32_t *p_str, int p_clip_to_len);
String(const StrRange &p_range);
String과 std::string 간의 상호 변환 논리를 편리하게 추가합니다.
头文件:
String(const std::string &str);
operator std::string();
源文件:
String::String(const std::string &str) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring formatted_wstring = converter.from_bytes(str);
copy_from(formatted_wstring.c_str());
}
String::operator std::string() {
std::string utf8;
const char32_t * utf32 = ptr();
for (char32_t c : utf32) {
if (c <= 0x7F) {
utf8.push_back(static_cast<char>(c));
} else if (c <= 0x7FF) {
utf8.push_back(static_cast<char>((c >> 6) | 0xC0));
utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
} else if (c <= 0xFFFF) {
utf8.push_back(static_cast<char>((c >> 12) | 0xE0));
utf8.push_back(static_cast<char>(((c >> 6) & 0x3F) | 0x80));
utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
} else if (c <= 0x10FFFF) {
utf8.push_back(static_cast<char>((c >> 18) | 0xF0));
utf8.push_back(static_cast<char>(((c >> 12) & 0x3F) | 0x80));
utf8.push_back(static_cast<char>(((c >> 6) & 0x3F) | 0x80));
utf8.push_back(static_cast<char>((c & 0x3F) | 0x80));
} else {
throw std::invalid_argument("Invalid UTF-32 character.");
}
}
return utf8;
}
이것은 훨씬 더 편리합니다.
예를 들어 Variant를 std::string으로 변환하려면 스파크와 번개를
Variant v;
...
std::string str = v.operator String().operator std::string();
사실 더 간단한 사용법도 있지만, 코드 프롬프트와 자동 완성의 경우에는 이런 방식으로 코드를 작성하는 것이 더 편합니다.