65.9K
CodeProject 正在变化。 阅读更多。
Home

Microsoft Access 和 Excel 中的印度货币格式设置

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2021 年 2 月 1 日

MIT

7分钟阅读

viewsIcon

5516

downloadIcon

74

使用 VBA 格式化货币和金额的印度数字格式

引言

货币值的印度数字格式化显示方式与其他任何地方都截然不同,如果您的 Windows 设置为其他(典型的西方)本地化,您将无法使用 VBA 的原生 Format 函数按照印度或巴基斯坦用户期望的方式格式化这些值。

这是一个例子

Value = 7534721.45
Typical format with grouping of three digits:    7,534,721.45
Indian format with grouping of two-three digits: 75,34,721.45

完整的文档可在维基百科上找到:印度数字系统

此处列出的表达式通过动态构建格式化字符串来解决此问题 - 对于任何数据类型的 Currency 值 - 该字符串由要显示的值控制,并返回浮点或固定小数。

无需特殊知识,但您应该熟悉 VBAFormat 函数。

动态格式化

当静态格式字符串无法满足用户期望显示的所有值时,将使用动态格式化。

这意味着,与其他四种 Format 函数原生可以处理的范围相比,需要对一个或多个值范围使用不同的格式,它们是:

  • 正数
  • 负片
  • Zero
  • Null (空值)

原因是组分隔符和十进制分隔符必须分别固定为逗号和点,因此不能应用整数部分和小数部分的常规格式。相反,使用固定逗号作为组分隔符,并使用 Str 函数来格式化十进制值,因为 Str 始终返回一个点作为十进制分隔符。

创建格式化值的最终表达式包含三个部分

  • 负值的引导符号(减号)
  • 包含零的任何值的整数部分
  • 小数部分

此外,小数部分还可以格式化为具有浮点小数固定小数

这三个部分连接起来形成返回的字符串,此处为浮点小数

Format(Value, ";-") & Format(Abs(Fix(Value)), Right("##\,##\,##\,##\,##\,##\,", 
((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4) & "##0") & IIf(Value - Fix(Value), 
LTrim(Str(Abs(Value - Fix(Value)))), "")

对于固定小数,它甚至更长

Format(Value, ";-") & Format(Abs(Fix(Value)), Right("##\,##\,##\,##\,##\,##\,", 
((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4) & "##0") & IIf(Value - Fix(Value), 
Left(LTrim(Str(Abs(Value - Fix(Value)))) & "0000", Sgn(Digits) + Digits), 
Mid(".0000", 2 - Sgn(Digits), Sgn(Digits) + Digits))   

这些表达式不适合扩展使用。在这种情况下,包装函数很方便

' Format a Currency value in the Indian number format.
'
' Value can be any value between the minimum and maximum of Currency:
'   Value = CCur("-922337203685477.5808")
'   Value = CCur(" 922337203685477.5807")
'
' Example:
'   922337203685477.5807 -> "92,23,37,20,36,85,477.5807"
'
' Digits controls the decimal count:
'   If Digits is less than 0, floating decimals is used.
'   If Digits is between 0 and 4, fixed decimals is used.
'
' Note:
'   For fixed decimals less than four, parameter Value
'   should be rounded before passed to this function.
'
' Source:
'   https://en.wikipedia.org/wiki/Indian_numbering_system
'
' 2021-01-31. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function FormatIndianCurrency( _
    ByVal Value As Currency, _
    Optional ByVal Digits As Integer = -1) _
    As String

    Const MaxDecimals   As Integer = 4
    
    Dim TextValue       As String

    If Digits < 0 Then
        ' Floating decimal.
        TextValue = _
            Format(Value, ";-") & _
            Format(Abs(Fix(Value)), _
                Right("##\,##\,##\,##\,##\,##\,", _
                ((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4) & _
                "##0") & _
            IIf(Value - Fix(Value), LTrim(Str(Abs(Value - Fix(Value)))), "")
    Else
        ' Fixed decimal.
        If Digits > MaxDecimals Then
            Digits = MaxDecimals
        End If
        TextValue = _
            Format(Value, ";-") & _
            Format(Abs(Fix(Value)), _
                Right("##\,##\,##\,##\,##\,##\,", _
                ((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4) & _
                "##0") & _
            IIf(Value - Fix(Value), _
                Left(LTrim(Str(Abs(Value - Fix(Value)))) & "0000", Sgn(Digits) + Digits), _
                Mid(".0000", 2 - Sgn(Digits), Sgn(Digits) + Digits))
    End If
    
    FormatIndianCurrency = TextValue
    
End Function

这可能看起来仍然有点复杂,所以让我们将其拆分。

符号部分

这很简单。其格式字符串 ";-" 在其正数部分(分号之前)没有代码,会忽略正数值并返回空字符串,而负数部分(分号之后)只包含一个减号。零部分(后面还有一个分号)被省略;与空的正数部分一样,这将导致零值返回空字符串。

总结:任何负值将被“格式化”为减号,任何其他值则为空字符串。

Value = -387.89
Format(Value, ";-") -> "-"

整数部分

第一个任务是获取整数和绝对值。必须使用 Fix 而不是 Int,因为它向零取整。

Value = -387.89
Abs(Fix(Value)) -> 387

现在是格式部分,一开始应该是这个字符串

    "##\,##\,##\,##\,##\,##\,##0"

但是,这仅对非常大的值有效,因为较小的值将返回一系列前导逗号。要解决此问题,必须动态地减小格式字符串以适应要格式化的值,并且必须每两个数字进行四个字符的步长

"##\,##\,##\,##\,##\,##\,##0"
    "##\,##\,##\,##\,##\,##0"
        "##\,##\,##\,##\,##0"
            "##\,##\,##\,##0"
                "##\,##\,##0"
                    "##\,##0"
                        "##0"

使用一些数学来截断格式字符串。CStr 的目的是将结果转换为字符串,以便 Len 进行测量。您可能认为这并非必需,但实际上是必需的,否则代码将无法编译。

((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4

步骤是

  • 获取绝对整数值的长度(数字的计数)
  • 减去 2 以表示百位
  • 除以二
  • 向下取整
  • 乘以四

然后 Right 将格式字符串截断到找到的长度

Right("##\,##\,##\,##\,##\,##\,", ((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4)

最后,将用于小数值的基础格式字符串附加到构建 Format 的完整格式字符串

Right("##\,##\,##\,##\,##\,##\,", ((Len(CStr(Abs(Fix(Value)))) - 2) \ 2) * 4) & "##0"

小数部分(浮点小数)

要仅返回小数部分,请减去整数部分,并使用 Str 将值转换为文本,前面加上固定的十进制分隔符(一个点)

Value = 11.345
Str(Value - Fix(Value)) -> " .345"

但是,负值会添加一个前导减号

Value = -11.345
Str(Value - Fix(Value)) -> "-.345"

为避免这种情况,使用绝对值

Value = -11.345
Str(Abs(Value - Fix(Value))) -> " .345"

最后,使用 LTrim 删除 Str 为非负值返回的前导空格

Value = -11.345
LTrim(Str(Abs(Value - Fix(Value)))) -> ".345"

至于上面示例中返回的字符串,请注意省略了前导零,我们不需要它。不幸的是,小数部分也可以为零(当传递整数值时),并且 Str 将返回零。

Str(0) -> " 0"

我们不想要这个,因为格式化整数部分的值也为零时返回“0”。为了省略它,使用了一个简单的条件,该条件检查值是否为零,如果是,则返回空字符串。

IIf(Value - Fix(Value), LTrim(Str(Abs(Value - Fix(Value)))), "")

小数部分(固定小数)

注意:此处描述的方法不会执行舍入,只会截断超出的小数或用零填充缺失的小数。如果必须舍入值,请在格式化之前进行。4/5 舍入的简单方法是使用 Format
Value = 7344.628
RoundedValue = CCur(Format(Value, "0.00"))
RoundedValue -> 7344.63 

对于其他需要精确度的舍入,请避免使用有 bug 的 Round,并使用 VBA.Round 中的函数。

如果您希望固定小数,例如两位小数,则必须应用扩展的小数部分表达式,并指定所需的小数位数(数字)。

如果小数部分不为零,则必须调整十进制值以匹配数字的计数。问题在于,如果存在十进制值,则必须包含十进制分隔符,因此数字计数 0, 1, 2, 3, 4 的序列应返回字符串长度 0, 2, 3, 4, 5Sgn 函数非常适合此目的,因为它为有一个或多个数字返回 1,否则返回 0(零);将其添加到数字计数中,即可获得正确的字符串长度。

在第一部分中,使用 Left 来截断有值的十进制数的长度

Left(LTrim(Str(Abs(Value - Fix(Value)))) & "0000", Sgn(Digits) + Digits)

在第二部分(对于没有小数的整数值),Mid 用于调整零值小数部分的长度

Mid(".0000", 2 - Sgn(Digits), Sgn(Digits) + Digits))

准备好这些部分后,最终的十进制表达式就可以组装了

IIf(Value - Fix(Value), Left(LTrim(Str(Abs(Value - Fix(Value)))) & "0000", 
Sgn(Digits) + Digits), Mid(".0000", 2 - Sgn(Digits), Sgn(Digits) + Digits))

示例输出

此表列出了从 Currency 数据类型的最大值到最小值为零的范围内的值的格式化输出,带有浮点小数

正值 格式化后的正值 负值 格式化后的负值
922337203685477.5807 92,23,37,20,36,85,477.5807 -922337203685477.5808 -92,23,37,20,36,85,477.5808
92233720368547.7581 9,22,33,72,03,68,547.7581 -92233720368547.7581 -9,22,33,72,03,68,547.7581
9223372036854.7758 92,23,37,20,36,854.7758 -9223372036854.7758 -92,23,37,20,36,854.7758
922337203685.4776 9,22,33,72,03,685.4776 -922337203685.4776 -9,22,33,72,03,685.4776
92233720368.5478 92,23,37,20,368.5478 -92233720368.5478 -92,23,37,20,368.5478
9223372036.8548 9,22,33,72,036.8548 -9223372036.8548 -9,22,33,72,036.8548
922337203.6855 92,23,37,203.6855 -922337203.6855 -92,23,37,203.6855
92233720.3685 9,22,33,720.3685 -92233720.3685 -9,22,33,720.3685
9223372.0369 92,23,372.0369 -9223372.0369 -92,23,372.0369
922337.2037 9,22,337.2037 -922337.2037 -9,22,337.2037
92233.7204 92,233.7204 -92233.7204 -92,233.7204
9223.372 9,223.372 -9223.372 -9,223.372
922.3372 922.3372 -922.3372 -922.3372
92.2337 92.2337 -92.2337 -92.2337
9.2234 9.2234 -9.2234 -9.2234
0.9223 0.9223 -0.9223 -0.9223
0.0922 0.0922 -0.0922 -0.0922
0.0092 0.0092 -0.0092 -0.0092
0.0009 0.0009 -0.0009 -0.0009
0.0001 0.0001 -0.0001 -0.0001
0 0 0 0

如果使用固定的两位小数格式,超出的小数将被截断,最后四行都将显示为 0.00

如果使用固定的四位小数格式,值为 9223.372 将输出为“9,223.3720”,最后一行显示为 0.0000

更多信息

有关 Format 函数及其格式部分的完整信息,请查阅官方文档:VBA Format 函数

结论

为了最好地服务于印度或巴基斯坦用户,他们期望大数以印度数字格式格式化,本文介绍了两种方法,并对其进行了说明,这些方法可在 Windows 未设置为提供此格式的本地化设置时使用。它们可以作为“单行代码”用于单个场景,也可以通过包含的包装函数用于更广泛的用途。此函数可用于 V BA 的原生 Format 函数。

历史

  • 2021 年 2 月 1 日:首次发布
© . All rights reserved.