【问题标题】:CDec: international syntax for argument string with decimalsCDec:带小数的参数字符串的国际语法
【发布时间】:2019-07-05 10:49:08
【问题描述】:

我的 Excel 的所有内容(公式、界面、指南)都设置为英文。 我的 Windows 设置为“,”作为十进制符号和“。”用于区域设置中的数字分组符号,因为我住在意大利。

如果我写这段代码:

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

结果在 Locals 窗口中显示为“12345678901234567050,123456789”(变量/十进制)。如果我使用 msgbox 也一样。

如果我使用“。”而不是“,”,结果是 12345678901234567000123456839。

在 VBA 中,当写数字时(以数字的形式,而不是字符串),我必须使用英文语法,即“。”用于十进制符号。

我相信我的示例代码会在具有英语区域设置的 Windows 上错误地运行。

我怎样才能(修改它以)使其在任何区域设置下都能正常运行?

【问题讨论】:

  • IIRC,VBA 将始终使用. 作为小数分隔符。只有当你应该输入小数时输入字符串,你的代码才会运行不正确。就像 VBA 将 #12/1/2000# 视为 12 月 1 日一样。这就是为什么您需要在使用数据源之前对其进行验证。它适用于所有系统中的所有代码。如果它不能以这种方式工作,编码将是不可能的,因为您需要在遇到不同的语言设置时立即更改它
  • 引用的代码可以在我的意大利语 Windows 上正常工作。将其作为字符串提供不仅有效,而且也是将比 Double 或 LongLong 数字更长/更精确的数字传递给 CDec 函数的唯一方法,这就是我的示例。
  • @Gareth 在 VBA 代码中硬编码的数字文字始终使用 . 作为小数分隔符。 ## 日期文字是另一种硬编码数字,无论如何也遵循相同的符号。但是C* 转换函数适用于字符串并与计算机的区域设置相关,因为它们旨在与用户交互,因此它们希望根据计算机区域设置对字符串进行格式化。
  • @6diegodiego9 这是一个非常好的问题。

标签: vba regional-settings


【解决方案1】:

C* 转换函数(CIntCLngCStr 等)都设计为在计算机的当前区域设置中工作。他们将使用当前的小数分隔符,因此您正确地假设 CDec 将无法在具有不同小数点的系统上正确处理硬编码的 ,

相反,StrVal 始终使用英文分隔符,但它们不支持 Decimal


所以想到的一种选择是获取小数点at runtime

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

但是应该注意,如果Application.UseSystemSeparatorsFalse 并且Application.DecimalSeparator 已更改,那么Application.International(xlDecimalSeparator)return that changed separator,而不是来自计算机区域设置的那个。所以如果你不能保证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

(有关对what the string is allowed to contain 有更多控制权的此代码版本,请参阅此答案的revision 3。接收NUMPRS_STD 的参数是要更改的参数。)

【讨论】:

  • 你为什么不打电话给VarDecFromStr而不是VarParseNumFromStr/VarNumFromParseNum
  • @FlorentB。因为我忽略了它,而且即使我没有,我也可能会因为它返回 DECIMAL 而忽略它,而没有意识到 DECIMALVARIANT 顶级联合的成员,所以它是等价的到VARIANT
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
相关资源
最近更新 更多