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





5.00/5 (2投票s)
使用 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
值 - 该字符串由要显示的值控制,并返回浮点或固定小数。
无需特殊知识,但您应该熟悉 VBA 和 Format
函数。
动态格式化
当静态格式字符串无法满足用户期望显示的所有值时,将使用动态格式化。
这意味着,与其他四种 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)))), "")
小数部分(固定小数)
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, 5
。Sgn
函数非常适合此目的,因为它为有一个或多个数字返回 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 日:首次发布