正确使用_tcstod并设置lpszEndChar是否为NULL
Using _tcstod correctly and setting lpszEndChar to NULL or not
我看到了答案 here 并阅读了原始文档。
所以他们的代码:
bool IsValidFloat(const CString& text, double& value)
{
LPCTSTR ptr = (LPCTSTR) text;
LPTSTR endptr;
value = _tcstod(ptr, &endptr);
return (*ptr && endptr - ptr == text.GetLength());
}
我的一些用户遇到了问题,我想我已将问题范围缩小为:
LPCTSTR lpszValue = (LPCTSTR)strWord ;
LPTSTR lpszEndChar = NULL ;
double dLineSpace = _tcstod(lpszValue, &lpszEndChar);
我注意到在上面的示例中 endptr
没有默认为 NULL
。也不是示例 here(他们只是使用 char *string, *stopstring;
)。
这是我的代码失败的原因吗?我们不能将 lpszEndChar
默认为 NULL
的具体原因是什么?
如果您给 strtod
函数族之一(其中包括 Windows/MSVC 构建中的 _tcstod
)一个 非 NULL 参数作为第二个 endptr
参数(即有效 pointer 变量的地址),则它不会(或 不应该) 不管调用函数时指针的地址 value(即使它是 NULL
):它是一个 'output-only' 参数。如果您将 actual NULL
作为第二个参数传递,那么函数将不会(不能)修改(未)指向的内容。
在您的情况下,您正在将 *endptr - str
(注意第一个取消引用)与整个字符串的长度进行比较,以检查有效读取。这假设数字后没有 'extra' 个字符(甚至是空格)。
此测试失败的一个更可能的原因是在错误的区域设置中给出了浮点数;因此,如果您的语言环境设置为默认值 "C"(期望点小数点),并且给出的数字为“12,34”(欧洲格式),那么 12
将被成功读取来自字符串,但 *endptr
将指向 3
.
要解决区域设置问题,有几个选项。首先(如果您知道 'origin' 语言环境),您可以在调用 _tcstod
之前使用 setlocale
函数来设置适当的十进制字符:
setlocale(LC_NUMERIC, FRENCH_LOCALE);
double dLineSpace = _tcstod(lpszValue, &lpszEndChar);
setlocale(LC_NUMERIC, "C"); // Revert to default, 'C' locale
或者,如果您不确定小数点是点还是逗号,您应该先将所有逗号替换为点。如果您的数据(最初)在 CString
变量中,而您 只有 有一个数字,那么您可以使用 CString::Replace
函数:
myString.Replace(_T(','), _T('.'));
//...
但是,如果您的 CString
比单个数字更复杂(或者不是 CString
),您将不得不编写一个小函数来进行替换为你。 (如果您需要的话,我也许可以提供提示!)
我看到了答案 here 并阅读了原始文档。
所以他们的代码:
bool IsValidFloat(const CString& text, double& value)
{
LPCTSTR ptr = (LPCTSTR) text;
LPTSTR endptr;
value = _tcstod(ptr, &endptr);
return (*ptr && endptr - ptr == text.GetLength());
}
我的一些用户遇到了问题,我想我已将问题范围缩小为:
LPCTSTR lpszValue = (LPCTSTR)strWord ;
LPTSTR lpszEndChar = NULL ;
double dLineSpace = _tcstod(lpszValue, &lpszEndChar);
我注意到在上面的示例中 endptr
没有默认为 NULL
。也不是示例 here(他们只是使用 char *string, *stopstring;
)。
这是我的代码失败的原因吗?我们不能将 lpszEndChar
默认为 NULL
的具体原因是什么?
如果您给 strtod
函数族之一(其中包括 Windows/MSVC 构建中的 _tcstod
)一个 非 NULL 参数作为第二个 endptr
参数(即有效 pointer 变量的地址),则它不会(或 不应该) 不管调用函数时指针的地址 value(即使它是 NULL
):它是一个 'output-only' 参数。如果您将 actual NULL
作为第二个参数传递,那么函数将不会(不能)修改(未)指向的内容。
在您的情况下,您正在将 *endptr - str
(注意第一个取消引用)与整个字符串的长度进行比较,以检查有效读取。这假设数字后没有 'extra' 个字符(甚至是空格)。
此测试失败的一个更可能的原因是在错误的区域设置中给出了浮点数;因此,如果您的语言环境设置为默认值 "C"(期望点小数点),并且给出的数字为“12,34”(欧洲格式),那么 12
将被成功读取来自字符串,但 *endptr
将指向 3
.
要解决区域设置问题,有几个选项。首先(如果您知道 'origin' 语言环境),您可以在调用 _tcstod
之前使用 setlocale
函数来设置适当的十进制字符:
setlocale(LC_NUMERIC, FRENCH_LOCALE);
double dLineSpace = _tcstod(lpszValue, &lpszEndChar);
setlocale(LC_NUMERIC, "C"); // Revert to default, 'C' locale
或者,如果您不确定小数点是点还是逗号,您应该先将所有逗号替换为点。如果您的数据(最初)在 CString
变量中,而您 只有 有一个数字,那么您可以使用 CString::Replace
函数:
myString.Replace(_T(','), _T('.'));
//...
但是,如果您的 CString
比单个数字更复杂(或者不是 CString
),您将不得不编写一个小函数来进行替换为你。 (如果您需要的话,我也许可以提供提示!)