Правильное использование _tcstod и установка lpszEndChar в NULL или нет - PullRequest
2 голосов
/ 30 апреля 2020

Я увидел ответ здесь и прочитал оригинальную документацию.

Итак, их код:

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. И пример здесь (где они просто используют char *string, *stopstring;).

По этой причине мой код не работает? Есть ли конкретная c причина, по которой мы не можем установить по умолчанию lpszEndChar в NULL?

1 Ответ

2 голосов
/ 30 апреля 2020

Если вы зададите в качестве второго из семейства функций strtod (включая _tcstod в Windows / MSV C builds) аргумент не NULL, endptr аргумент (то есть адрес действительного указателя переменной), тогда он не имеет значения (или не должен ) иметь значение, какой адрес значение этот указатель имеет (даже если он NULL) при вызове функции: это аргумент «только для вывода». Если вы передадите фактический NULL в качестве второго аргумента, то функции не будут (не могут) изменять то, на что (не) указано.

В вашем случае, вы сравниваете *endptr - str (обратите внимание на разыменование на первом) с длиной всей строки, чтобы проверить правильность чтения. Это делает предположение, что после числа нет никаких «лишних» символов (даже пробелов).

Более вероятной причиной неудачного теста является то, что числа с плавающей точкой задаются в неправильной локали; поэтому, если для вашей локали задано значение по умолчанию "C" (с учетом десятичной точки), а число указано как "12,34" (в европейском формате), то 12 будет успешно прочитано из строка, но *endptr будет указывать на 3.

Чтобы решить проблему локали, есть несколько вариантов. Во-первых (если вы знаете локаль 'origin'), вы можете использовать функцию setlocale перед вызовом _tcstod, чтобы установить соответствующий десятичный символ:

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), вам придется написать небольшую функцию, чтобы сделать замену (и) за вас. (Я могу предложить подсказку, если это то, что вам нужно!)

...