#include "Utf8Util.hpp" #include #include #include #include #include namespace jdi { namespace { /** @brief 判断字符是否为需要裁掉的空白字符。 */ bool IsSpaceChar(wchar_t c) { return c == L' ' || c == L'\t' || c == L'\r' || c == L'\n' || c == L'\v' || c == L'\f' || c == static_cast(0x00A0); } /** @brief 判断字符是否为 ASCII 大写字母。 */ bool IsUpperAscii(wchar_t c) { return c >= L'A' && c <= L'Z'; } } std::wstring Utf8ToWide(const char* text, int length) { if (text == nullptr) { return std::wstring(); } int byteCount = (length < 0) ? static_cast(std::strlen(text)) : length; if (byteCount <= 0) { return std::wstring(); } int wideCount = MultiByteToWideChar(CP_UTF8, 0, text, byteCount, nullptr, 0); if (wideCount <= 0) { return std::wstring(); } std::wstring result(static_cast(wideCount), L'\0'); MultiByteToWideChar(CP_UTF8, 0, text, byteCount, &result[0], wideCount); return result; } std::string WideToUtf8(const wchar_t* text) { if (text == nullptr) { return std::string(); } int wideCount = static_cast(std::wcslen(text)); if (wideCount <= 0) { return std::string(); } int byteCount = WideCharToMultiByte(CP_UTF8, 0, text, wideCount, nullptr, 0, nullptr, nullptr); if (byteCount <= 0) { return std::string(); } std::string result(static_cast(byteCount), '\0'); WideCharToMultiByte(CP_UTF8, 0, text, wideCount, &result[0], byteCount, nullptr, nullptr); return result; } std::wstring Trim(const std::wstring& text) { size_t begin = 0; size_t end = text.size(); while (begin < end && IsSpaceChar(text[begin])) { ++begin; } while (end > begin && IsSpaceChar(text[end - 1])) { --end; } return text.substr(begin, end - begin); } std::wstring StripQuotes(const std::wstring& text) { std::wstring trimmed = Trim(text); if (trimmed.size() >= 2) { const wchar_t first = trimmed.front(); const wchar_t last = trimmed.back(); if ((first == L'"' && last == L'"') || (first == L'\'' && last == L'\'')) { trimmed = trimmed.substr(1, trimmed.size() - 2); } } return trimmed; } std::wstring ToLowerAscii(const std::wstring& text) { std::wstring result = text; for (wchar_t& c : result) { if (IsUpperAscii(c)) { c = static_cast(c - L'A' + L'a'); } } return result; } bool StartsWith(const std::wstring& text, const std::wstring& prefix) { if (prefix.size() > text.size()) { return false; } return text.compare(0, prefix.size(), prefix) == 0; } bool ContainsInsensitive(const std::wstring& text, const std::wstring& needle) { if (needle.empty()) { return true; } if (needle.size() > text.size()) { return false; } const size_t last = text.size() - needle.size(); for (size_t i = 0; i <= last; ++i) { size_t j = 0; while (j < needle.size()) { wchar_t a = text[i + j]; wchar_t b = needle[j]; if (IsUpperAscii(a)) { a = static_cast(a - L'A' + L'a'); } if (IsUpperAscii(b)) { b = static_cast(b - L'A' + L'a'); } if (a != b) { break; } ++j; } if (j == needle.size()) { return true; } } return false; } bool TryParseDouble(const char* begin, const char* end, double& value) { if (begin == nullptr || end == nullptr || begin >= end) { return false; } const char* p = begin; while (p < end && (*p == ' ' || *p == '\t')) { ++p; } const char* q = end; while (q > p && (q[-1] == ' ' || q[-1] == '\t' || q[-1] == '\r' || q[-1] == '\n')) { --q; } if (p >= q) { return false; } char buffer[96]; const size_t length = static_cast(q - p); const char* text = nullptr; std::string dynamic; if (length < sizeof(buffer)) { std::memcpy(buffer, p, length); buffer[length] = '\0'; text = buffer; } else { dynamic.assign(p, q); text = dynamic.c_str(); } char* stop = nullptr; const double parsed = std::strtod(text, &stop); if (stop == text) { return false; } value = parsed; return true; } std::wstring FormatNumber(double value, int decimals) { if (decimals < 0) { decimals = 0; } if (decimals > 12) { decimals = 12; } wchar_t buffer[96]; swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.*f", decimals, value); return std::wstring(buffer); } std::wstring FormatValue(double value) { if (std::isnan(value)) { return L"NaN"; } if (std::isinf(value)) { return (value > 0.0) ? L"+Inf" : L"-Inf"; } wchar_t buffer[96]; swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.6g", value); return std::wstring(buffer); } std::wstring FormatTick(double value, double step) { if (value == 0.0) { return L"0"; } if (!std::isfinite(value)) { return FormatValue(value); } int decimals = 0; if (step > 0.0 && std::isfinite(step)) { const double magnitude = std::ceil(-std::log10(step) - 1.0e-9); if (magnitude > 0.0) { decimals = static_cast(magnitude); } } if (decimals > 9) { decimals = 9; } wchar_t buffer[96]; swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.*f", decimals, value); std::wstring text(buffer); if (text.size() > 15) { swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.4e", value); text.assign(buffer); } return text; } std::wstring FormatCount(unsigned long long value) { wchar_t buffer[32]; swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%llu", value); std::wstring digits(buffer); std::wstring result; result.reserve(digits.size() + digits.size() / 3); const size_t length = digits.size(); for (size_t i = 0; i < length; ++i) { if (i > 0 && ((length - i) % 3u) == 0u) { result.push_back(L','); } result.push_back(digits[i]); } return result; } double NiceStep(double range, int targetTicks) { if (!(range > 0.0) || !std::isfinite(range) || targetTicks <= 0) { return 1.0; } const double raw = range / static_cast(targetTicks); const double magnitude = std::pow(10.0, std::floor(std::log10(raw))); const double normalized = raw / magnitude; double nice = 10.0; if (normalized <= 1.0) { nice = 1.0; } else if (normalized <= 2.0) { nice = 2.0; } else if (normalized <= 5.0) { nice = 5.0; } return nice * magnitude; } void BuildTicks(double minValue, double maxValue, double step, std::vector& ticks) { ticks.clear(); if (!std::isfinite(minValue) || !std::isfinite(maxValue) || !(step > 0.0) || !std::isfinite(step)) { return; } if (maxValue < minValue) { const double temp = minValue; minValue = maxValue; maxValue = temp; } const long long first = static_cast(std::ceil(minValue / step - 1.0e-9)); const long long last = static_cast(std::floor(maxValue / step + 1.0e-9)); if (last < first) { return; } const long long count = last - first + 1; if (count > 512) { return; } ticks.reserve(static_cast(count)); for (long long index = first; index <= last; ++index) { ticks.push_back(static_cast(index) * step); } } bool CopyTextToClipboard(HWND owner, const std::wstring& text) { if (text.empty()) { return false; } if (OpenClipboard(owner) == FALSE) { return false; } bool success = false; if (EmptyClipboard() != FALSE) { const SIZE_T bytes = (text.size() + 1) * sizeof(wchar_t); HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE, bytes); if (handle != nullptr) { void* target = GlobalLock(handle); if (target != nullptr) { std::memcpy(target, text.c_str(), bytes); GlobalUnlock(handle); if (SetClipboardData(CF_UNICODETEXT, handle) != nullptr) { success = true; } else { GlobalFree(handle); } } else { GlobalFree(handle); } } } CloseClipboard(); return success; } bool PasteTextFromClipboard(HWND owner, std::wstring& text) { text.clear(); if (OpenClipboard(owner) == FALSE) { return false; } bool success = false; if (IsClipboardFormatAvailable(CF_UNICODETEXT) != FALSE) { HANDLE handle = GetClipboardData(CF_UNICODETEXT); if (handle != nullptr) { const void* source = GlobalLock(handle); if (source != nullptr) { const wchar_t* wide = static_cast(source); text.assign(wide); GlobalUnlock(handle); success = !text.empty(); } } } CloseClipboard(); // 单行输入框:把换行与制表符折叠为空格,避免内容被截断显示。 if (success) { for (wchar_t& c : text) { if (c == L'\r' || c == L'\n' || c == L'\t') { c = L' '; } } } return success; } } // namespace jdi