Merge pull request #8541 from jordan-woyak/float-parse-fix
StringUtil: Make TryParse of floats handle comma and dot decimal separators.
This commit is contained in:
commit
f35f4f2bf0
|
@ -235,51 +235,6 @@ std::string_view StripQuotes(std::string_view s)
|
|||
return s;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u16* const output)
|
||||
{
|
||||
u64 value;
|
||||
if (!TryParse(str, &value))
|
||||
return false;
|
||||
|
||||
if (value >= 0x10000ull && value <= 0xFFFFFFFFFFFF0000ull)
|
||||
return false;
|
||||
|
||||
*output = static_cast<u16>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u32* const output)
|
||||
{
|
||||
u64 value;
|
||||
if (!TryParse(str, &value))
|
||||
return false;
|
||||
|
||||
if (value >= 0x100000000ull && value <= 0xFFFFFFFF00000000ull)
|
||||
return false;
|
||||
|
||||
*output = static_cast<u32>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, u64* const output)
|
||||
{
|
||||
char* end_ptr = nullptr;
|
||||
|
||||
// Set errno to a clean slate
|
||||
errno = 0;
|
||||
|
||||
u64 value = strtoull(str.c_str(), &end_ptr, 0);
|
||||
|
||||
if (end_ptr == nullptr || *end_ptr != '\0')
|
||||
return false;
|
||||
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
*output = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryParse(const std::string& str, bool* const output)
|
||||
{
|
||||
float value;
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
#include <cstdarg>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
@ -47,20 +49,60 @@ std::string ArrayToString(const u8* data, u32 size, int line_len = 20, bool spac
|
|||
std::string_view StripSpaces(std::string_view s);
|
||||
std::string_view StripQuotes(std::string_view s);
|
||||
|
||||
std::string ReplaceAll(std::string result, std::string_view src, std::string_view dest);
|
||||
|
||||
bool TryParse(const std::string& str, bool* output);
|
||||
bool TryParse(const std::string& str, u16* output);
|
||||
bool TryParse(const std::string& str, u32* output);
|
||||
bool TryParse(const std::string& str, u64* output);
|
||||
|
||||
template <typename N>
|
||||
static bool TryParse(const std::string& str, N* const output)
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>* = nullptr>
|
||||
bool TryParse(const std::string& str, T* output)
|
||||
{
|
||||
std::istringstream iss(str);
|
||||
// is this right? not doing this breaks reading floats on locales that use different decimal
|
||||
// separators
|
||||
iss.imbue(std::locale("C"));
|
||||
char* end_ptr = nullptr;
|
||||
|
||||
N tmp;
|
||||
// Set errno to a clean slate.
|
||||
errno = 0;
|
||||
|
||||
// Read a u64 for unsigned types and s64 otherwise.
|
||||
using ReadType = std::conditional_t<std::is_unsigned_v<T>, u64, s64>;
|
||||
ReadType value;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
value = std::strtoull(str.c_str(), &end_ptr, 0);
|
||||
else
|
||||
value = std::strtoll(str.c_str(), &end_ptr, 0);
|
||||
|
||||
// Fail if the end of the string wasn't reached.
|
||||
if (end_ptr == nullptr || *end_ptr != '\0')
|
||||
return false;
|
||||
|
||||
// Fail if the value was out of 64-bit range.
|
||||
if (errno == ERANGE)
|
||||
return false;
|
||||
|
||||
using LimitsType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
|
||||
std::common_type<T>>::type;
|
||||
// Fail if outside numeric limits.
|
||||
if (value < std::numeric_limits<LimitsType>::min() ||
|
||||
value > std::numeric_limits<LimitsType>::max())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*output = static_cast<T>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr>
|
||||
bool TryParse(std::string str, T* const output)
|
||||
{
|
||||
// Replace commas with dots.
|
||||
std::istringstream iss(ReplaceAll(std::move(str), ",", "."));
|
||||
|
||||
// Use "classic" locale to force a "dot" decimal separator.
|
||||
iss.imbue(std::locale::classic());
|
||||
|
||||
T tmp;
|
||||
|
||||
// Succeed if a value was read and the entire string was used.
|
||||
if (iss >> tmp && iss.eof())
|
||||
{
|
||||
*output = tmp;
|
||||
|
@ -118,7 +160,6 @@ bool SplitPath(std::string_view full_path, std::string* path, std::string* filen
|
|||
|
||||
void BuildCompleteFilename(std::string& complete_filename, std::string_view path,
|
||||
std::string_view filename);
|
||||
std::string ReplaceAll(std::string result, std::string_view src, std::string_view dest);
|
||||
|
||||
bool StringBeginsWith(std::string_view str, std::string_view begin);
|
||||
bool StringEndsWith(std::string_view str, std::string_view end);
|
||||
|
|
Loading…
Reference in New Issue