CDec: международный синтаксис строки аргумента с десятичными знаками - PullRequest
2 голосов
/ 05 июля 2019

Мой Excel установлен на английском языке для всего (формула, интерфейс, руководство).Моя Windows установлена ​​с "," для десятичного символа и с "."для символа группировки цифр в региональных настройках, как я живу в Италии.

Если я напишу этот код:

    Dim v As Variant
    v = CDec("12345678901234567000,123456789")
    v = v + 50

, результат отобразится как "12345678901234567050,123456789" (вариант / десятичное число) вОкно местных жителей.То же самое, если я сообщаю об этом.

Если я использую "."вместо "," результат равен 12345678901234567000123456839.

В VBA при записи чисел (в виде чисел, а не строк) я должен использовать синтаксис английского языка, т. е. "."для десятичного символа.

Я считаю, что этот код моего примера будет некорректно работать в Windows с региональными настройками на английском языке.

Как я могу (изменить его), чтобы он работал правильно с любым региональнымнастройки?

1 Ответ

1 голос
/ 05 июля 2019

Функции преобразования C* (CInt, CLng, CStr и т. Д.) Предназначены для работы в текущей локали компьютера. Они будут использовать текущий десятичный разделитель, поэтому вы правы, предполагая, что CDec не сможет правильно обработать жестко закодированный , в системе с другой десятичной точкой.

Напротив, Str и Val всегда работают с английскими разделителями, но они не поддерживают Decimal.


Поэтому на ум приходит один вариант - получить десятичную точку во время выполнения :

Dim v As Variant
v = CDec("12345678901234567000" & Application.International(xlDecimalSeparator) & "123456789")

Следует отметить, однако, что если Application.UseSystemSeparators равно False и Application.DecimalSeparator было изменено, то Application.International(xlDecimalSeparator) будет возвращать этот измененный разделитель , а не тот, который получен из языкового стандарта компьютера. Поэтому не используйте этот метод, если вы не можете гарантировать, что UseSystemSeparators равно True.


Другой вариант - выразить десятичную позицию в виде деления степенью десяти, что хорошо с точным типом данных с фиксированной точкой Decimal:

Dim v As Variant
v = CDec("12345678901234567000123456789") / CDec("1000000000")

Еще один вариант - иметь собственный «CDec», который явно работает в определенной локали и всегда жестко кодировать строки в этой локали:

Option Explicit

#If VBA7 Then
Private Declare PtrSafe Function VarDecFromStr Lib "OleAut32.dll" (ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdecOut As Variant) As Long
#Else
Private Declare Function VarDecFromStr Lib "OleAut32.dll" (ByVal strIn As Long, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdecOut As Variant) As Long
#End If

Private Const LOCALE_INVARIANT As Long = &H7F&
Private Const S_OK As Long = &H0

Public Function ParseDecimalFromEnUsString(ByVal s As String) As Variant
  Dim hr As Long

  hr = VarDecFromStr(StrPtr(s), LOCALE_INVARIANT, 0, ParseDecimalFromEnUsString)

  If hr <> S_OK Then
    Err.Raise 5, , "Cannot parse the string. Error " & Hex$(hr)
  End If
End Function
? ParseDecimalFromEnUsString("12345678901234567000.123456789")
12345678901234567000,123456789

? TypeName(ParseDecimalFromEnUsString("12345678901234567000.123456789"))
Decimal

(Для версии этого кода с большим контролем над , что строка может содержать , см. редакция 3 этого ответа. Параметр, который получает NUMPRS_STD тот, который нужно изменить.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...