Как преобразовать число с плавающей точкой в ​​строку независимо от региональных настроек? - PullRequest
2 голосов
/ 03 февраля 2009

Мой продукт предназначен для португальской аудитории, где запятая является десятичным символом. Я обычно использую CString :: Format для ввода чисел в строки, и он учитывает региональные настройки компьютера. Хотя в целом это хороший подход, у меня возникают проблемы при форматировании запросов SQL, например:

CString szInsert;
szInsert.Format("INSERT INTO Vertices (X, Y) VALUES (%f, %f)", pt.X, pt.Y);

Когда значения передаются, я получаю строку, которая является неправильным запросом:

INSERT INTO Vertices (X, Y) VALUES (3,56, 4,67)

Как заставить точку в качестве десятичного символа в этих строках без изменения региональных настроек и без необходимости создавать специализированные строки для каждого значения с плавающей запятой?

Примечание: это общий вопрос, а не вопрос SQL.

Ответы [ 5 ]

10 голосов
/ 03 февраля 2009

Плохая идея, вы действительно должны использовать готовые заявления. Внедрение SQL с использованием только чисел не является тривиальным, но CString :: Format просто не является правильным способом связывания параметров.

(MFC и SQL уже давно - оказывается, это чертовски хорошо спрятано. Я начинаю видеть, как мы закончили с ошибками SQL-инъекций, спасибо Microsoft. С сырым ODBC вы создаете оператор (один раз) с SQLPrepare Передайте? Для 2 параметров, которые вы хотите заполнить. Впоследствии для каждого вызова INSERT SQLBindParameter(stmt, 1, &X); SQLBindParameter(stmt, 2, &Y) /*extra parameters omitted, see http://msdn.microsoft.com/en-us/library/ms710963(VS.85).aspx */. Наконец, вызовите SQLExecute, чтобы выполнить операцию.)

9 голосов
/ 03 февраля 2009

Комментарий о предложении Pukku с ostringstream: чтобы он не зависел от локали, нужно явно напечатать () поток желаемой локалью:

std::ostringstream s;
s.imbue(std::locale::classic());
s << "INSERT INTO Vertices (X, Y) VALUES (" << pt.X << ", " << pt.Y << ")";

В противном случае используется текущая глобальная локаль.

6 голосов
/ 03 февраля 2009

Параметризованные запросы должны полностью избежать этой проблемы. Вы должны посмотреть на них. Тем не менее, вы должны иметь возможность использовать setlocale или подобное для изменения десятичного разделителя.

1 голос
/ 06 октября 2014

Используйте

_create_locale( LC_NUMERIC, "C" )

, чтобы создать «английский» (по умолчанию C) язык и затем передать его одной из групп функций _sprintf_l.

, например

_locale_t locale = _create_locale( LC_NUMERIC, "C" );
_sprintf_l( pszValue, "%f", locale, 3.141 );
_free_locale(locale);

Это потокобезопасно. Локаль может храниться в статической переменной, чтобы избежать ее создания каждый раз, когда вам нужно отформатировать значение.

1 голос
/ 29 сентября 2009

Вот что я сделал.

CString FormatQuery(LPCTSTR pszFormat, ...)
{
    CString szLocale = setlocale(LC_NUMERIC, NULL);
    setlocale(LC_NUMERIC, "English");

    va_list args;
    va_start(args, pszFormat);
    CString szFormatted;
    int nSize = (_vscprintf(pszFormat, args) + 1) * sizeof(char);
    _vsnprintf_s(szFormatted.GetBuffer(nSize), nSize, nSize, pszFormat, args);
    szFormatted.ReleaseBuffer();
    va_end(args);

    setlocale(LC_NUMERIC, szLocale);
    return szFormatted;
}

Вы должны использовать его как sprintf. Вы должны #include <locale.h>, чтобы он заработал.

Я немного упрям, поэтому я не использовал подготовленные операторы / параметризованные запросы. Если у вас есть похожая проблема, я предлагаю вам сделать это. Между тем, если ваша проблема не связана с SQL, мой ответ должен помочь.

Редактировать: Вот версия с защитой потока:

CString FormatQuery(LPCTSTR pszFormat, ...)
{
    _locale_t locale = _create_locale(LC_NUMERIC, "English");

    va_list args;
    va_start(args, pszFormat);
    CString szFormatted;
    int nSize = (_vscprintf_l(pszFormat, locale, args) + 1) * sizeof(char);
    _vsnprintf_s_l(szFormatted.GetBuffer(nSize), nSize, nSize, pszFormat, locale, args);
    szFormatted.ReleaseBuffer();
    va_end(args);

    return szFormatted;
}
...