对于不同的区域设置,IsNumeric 在 excel/vba 中给出了意想不到的结果
IsNumeric gives unexpected results in excel/vba for different regional settings
我的印象是 IsNumeric(MyString)
会显示 Val(MyString)
是否预计会失败。根据区域设置,我发现了意想不到的差异。
示例1,瑞典区域设置(使用,
作为小数点分隔符):
IsNumeric("1.1")
=> 正确
Val("1.1")
=> 1.1
IsNumeric("1,1")
=> 正确
Val("1,1")
=> 1
示例2,爱沙尼亚区域设置(也使用,
作为小数点分隔符):
IsNumeric("1.1")
=> 假
Val("1.1")
=> 1.1
IsNumeric("1,1")
=> 正确
Val("1,1")
=> 1
我将字符串转换为数字的典型通用代码是:
Function ConvertToNumber(MyNumber as String) as Double
If IsNumeric(MyNumber) then
ConvertToNumber = Val(Replace(MyNumber, ",", "."))
Else
MsgBox "Invalid format!"
End If
End Function
但这在爱沙尼亚区域设置中意外失败。知道这是否是预期的行为吗?微软对 IsNumber 正在做什么的解释也有点含糊。你建议改用什么? If Val(MyNumber & "1")<>0
?对于某些特殊情况,例如 0E+0
,这将失败。还可以考虑从以下位置捕获任何错误:
CDbl(Replace(MyString, ".", Application.International(xlDecimalSeparator))
我可以使用正则表达式,但必须有更好的方法。
我将不胜感激。
/乔纳斯
不幸的是, 我不熟悉绕过那些特定区域设置以使用内置函数 IsNumeric()
。但是幸运的是,我可以帮助您在正则表达式的帮助下创建您自己的自定义函数。
您可以使用以下功能。将它添加到标准代码模块后,只需使用 IsNumber("1.1")
而不是 IsNumeric("1.1")
.
Function IsNumber(ByVal TestString As Variant) As Boolean
With CreateObject("VBScript.RegExp")
.Pattern = "^(?:\d+[.,]?\d*|\d*[.,]\d+)$"
IsNumber = .test(TestString)
End With
End Function
当然,如果其他人提出更直接的方法,我建议使用该方法。但是,这应该对您有用。
让我们快速分解一下模式 ^(?:\d+[.,]?\d*|\d*[.,]\d+)$
。此模式有一个 OR 运算符,即 |
符号,因此我们将单独查看它们。
^
断言这是字符串的开头。
(?:...)
是非捕获组。这只是为了我们不必为每个 OR |
语句添加字符串开头 ^
和字符串结尾 $
锚点。
\d+
匹配任何数字字符 \d
,一次或多次 +
,然后紧接着是
[.,]?
是一个字符组,它将匹配该组中的任何字符,零或一个次?
(使其成为可选),然后紧跟
\d*
,它将匹配任何数字 \d
、零 或 更多 次 *
(本质上是可选的,但可以有无限个字符)
- 最后,我们有字符串锚点的结尾
$
OR 语句的第二部分本质上是相同的,但我将第一个 \d
设为可选,以允许诸如 .1
之类的事情(注意 [=34 前面没有数字) =]) 仍然匹配为数字。
综上所述,现在您可以将代码更新为:
Function ConvertToNumber(MyNumber as String) as Double
If IsNumber(MyNumber) then
'Your code
Else
'Your code
End If
End Function
尽管如果是我的代码,我会在发送到您的转换函数之前检查数值:
If IsNumber(MyNumber) Then
Debug.Print ConvertToNumber(MyNumber)
Else
'Handle it here
End If
虽然这并没有太大的区别,但这对我个人来说更重要。
问题是使用 Val function 而
Returns the numbers contained in a string as a numeric value of appropriate type.
所以它不是您所期望的,因为它不会将字符串转换为数字,而是提取字符串中包含的数字直到第一个不是空格的非数字字符。
这意味着
Val(" 1615 198th Street N.E.")
将 return 1615198
作为两倍。
所以 Val
在所有本地化中解释为小数的始终是 .
。这意味着它将始终将 Val("1.1")
视为数字,但在 Val("1,1")
中,逗号是第一个非数字、非空白字符,因此它停在那里并且 returns 1
只有.
您正在寻找的是 CDbl
,它实际上使用您系统的小数分隔符 将字符串 转换为数字。
Option Explicit
Function ConvertToNumber(ByVal MyNumber As String) As Double
Dim RegionalNumber As String
RegionalNumber = MyNumber
' if you want the user to be able to enter dots as well as commas
' make sure all dots and commas are converted to the decimal seperator of your system
' otherwise go without the conversion
RegionalNumber = Replace$(RegionalNumber, ".", Application.DecimalSeparator)
RegionalNumber = Replace$(RegionalNumber, ",", Application.DecimalSeparator)
If IsNumeric(RegionalNumber) Then
ConvertToNumber = CDbl(RegionalNumber)
Else
MsgBox "Invalid format!"
End If
End Function
如果你用
测试这个
Sub test()
Debug.Print ConvertToNumber("1.1")
Debug.Print ConvertToNumber("1,1")
End Sub
在逗号作为小数点分隔符的系统中,它应该两次 return 1,1
作为数字。
解释为什么你的测试 return 编辑了他们 return 编辑的结果
示例 1,瑞典区域设置(使用“,”作为小数点分隔符):
IsNumeric("1.1")
=> 正确
因为 .
被认为是日期分隔符所以它是一个有效的数字
Val("1.1")
=> 1.1
因为 val 始终将点视为小数点分隔符
IsNumeric("1,1")
=> 正确
因为 ,
被认为是 十进制 分隔符
Val("1,1")
=> 1
因为 val 总是将逗号视为非数字字符
示例2,爱沙尼亚语区域设置(也使用','作为小数点分隔符):
IsNumeric("1.1")
=> 假
因为爱沙尼亚语中的点 不是 被视为日期分隔符,所以这不是有效数字
Val("1.1")
=> 1.1
因为 val 始终将点视为小数点分隔符
IsNumeric("1,1")
=> 正确
因为 ,
被认为是小数分隔符
Val("1,1")
=> 1
因为 val 总是将逗号视为非数字字符
请注意 IsNumeric
接受千位分隔符、日期分隔符和小数点分隔符以及货币符号。
我的印象是 IsNumeric(MyString)
会显示 Val(MyString)
是否预计会失败。根据区域设置,我发现了意想不到的差异。
示例1,瑞典区域设置(使用,
作为小数点分隔符):
IsNumeric("1.1")
=> 正确Val("1.1")
=> 1.1IsNumeric("1,1")
=> 正确Val("1,1")
=> 1
示例2,爱沙尼亚区域设置(也使用,
作为小数点分隔符):
IsNumeric("1.1")
=> 假Val("1.1")
=> 1.1IsNumeric("1,1")
=> 正确Val("1,1")
=> 1
我将字符串转换为数字的典型通用代码是:
Function ConvertToNumber(MyNumber as String) as Double
If IsNumeric(MyNumber) then
ConvertToNumber = Val(Replace(MyNumber, ",", "."))
Else
MsgBox "Invalid format!"
End If
End Function
但这在爱沙尼亚区域设置中意外失败。知道这是否是预期的行为吗?微软对 IsNumber 正在做什么的解释也有点含糊。你建议改用什么? If Val(MyNumber & "1")<>0
?对于某些特殊情况,例如 0E+0
,这将失败。还可以考虑从以下位置捕获任何错误:
CDbl(Replace(MyString, ".", Application.International(xlDecimalSeparator))
我可以使用正则表达式,但必须有更好的方法。
我将不胜感激。
/乔纳斯
不幸的是, 我不熟悉绕过那些特定区域设置以使用内置函数 IsNumeric()
。但是幸运的是,我可以帮助您在正则表达式的帮助下创建您自己的自定义函数。
您可以使用以下功能。将它添加到标准代码模块后,只需使用 IsNumber("1.1")
而不是 IsNumeric("1.1")
.
Function IsNumber(ByVal TestString As Variant) As Boolean
With CreateObject("VBScript.RegExp")
.Pattern = "^(?:\d+[.,]?\d*|\d*[.,]\d+)$"
IsNumber = .test(TestString)
End With
End Function
当然,如果其他人提出更直接的方法,我建议使用该方法。但是,这应该对您有用。
让我们快速分解一下模式 ^(?:\d+[.,]?\d*|\d*[.,]\d+)$
。此模式有一个 OR 运算符,即 |
符号,因此我们将单独查看它们。
^
断言这是字符串的开头。(?:...)
是非捕获组。这只是为了我们不必为每个 OR|
语句添加字符串开头^
和字符串结尾$
锚点。\d+
匹配任何数字字符\d
,一次或多次+
,然后紧接着是[.,]?
是一个字符组,它将匹配该组中的任何字符,零或一个次?
(使其成为可选),然后紧跟\d*
,它将匹配任何数字\d
、零 或 更多 次*
(本质上是可选的,但可以有无限个字符)- 最后,我们有字符串锚点的结尾
$
OR 语句的第二部分本质上是相同的,但我将第一个 \d
设为可选,以允许诸如 .1
之类的事情(注意 [=34 前面没有数字) =]) 仍然匹配为数字。
综上所述,现在您可以将代码更新为:
Function ConvertToNumber(MyNumber as String) as Double
If IsNumber(MyNumber) then
'Your code
Else
'Your code
End If
End Function
尽管如果是我的代码,我会在发送到您的转换函数之前检查数值:
If IsNumber(MyNumber) Then
Debug.Print ConvertToNumber(MyNumber)
Else
'Handle it here
End If
虽然这并没有太大的区别,但这对我个人来说更重要。
问题是使用 Val function 而
Returns the numbers contained in a string as a numeric value of appropriate type.
所以它不是您所期望的,因为它不会将字符串转换为数字,而是提取字符串中包含的数字直到第一个不是空格的非数字字符。
这意味着
Val(" 1615 198th Street N.E.")
将 return 1615198
作为两倍。
所以 Val
在所有本地化中解释为小数的始终是 .
。这意味着它将始终将 Val("1.1")
视为数字,但在 Val("1,1")
中,逗号是第一个非数字、非空白字符,因此它停在那里并且 returns 1
只有.
您正在寻找的是 CDbl
,它实际上使用您系统的小数分隔符 将字符串 转换为数字。
Option Explicit
Function ConvertToNumber(ByVal MyNumber As String) As Double
Dim RegionalNumber As String
RegionalNumber = MyNumber
' if you want the user to be able to enter dots as well as commas
' make sure all dots and commas are converted to the decimal seperator of your system
' otherwise go without the conversion
RegionalNumber = Replace$(RegionalNumber, ".", Application.DecimalSeparator)
RegionalNumber = Replace$(RegionalNumber, ",", Application.DecimalSeparator)
If IsNumeric(RegionalNumber) Then
ConvertToNumber = CDbl(RegionalNumber)
Else
MsgBox "Invalid format!"
End If
End Function
如果你用
测试这个Sub test()
Debug.Print ConvertToNumber("1.1")
Debug.Print ConvertToNumber("1,1")
End Sub
在逗号作为小数点分隔符的系统中,它应该两次 return 1,1
作为数字。
解释为什么你的测试 return 编辑了他们 return 编辑的结果
示例 1,瑞典区域设置(使用“,”作为小数点分隔符):
IsNumeric("1.1")
=> 正确
因为.
被认为是日期分隔符所以它是一个有效的数字Val("1.1")
=> 1.1
因为 val 始终将点视为小数点分隔符IsNumeric("1,1")
=> 正确
因为,
被认为是 十进制 分隔符Val("1,1")
=> 1 因为 val 总是将逗号视为非数字字符
示例2,爱沙尼亚语区域设置(也使用','作为小数点分隔符):
IsNumeric("1.1")
=> 假
因为爱沙尼亚语中的点 不是 被视为日期分隔符,所以这不是有效数字Val("1.1")
=> 1.1
因为 val 始终将点视为小数点分隔符IsNumeric("1,1")
=> 正确
因为,
被认为是小数分隔符Val("1,1")
=> 1 因为 val 总是将逗号视为非数字字符
请注意 IsNumeric
接受千位分隔符、日期分隔符和小数点分隔符以及货币符号。