【发布时间】:2010-11-01 09:56:55
【问题描述】:
我需要验证 textbox 输入并且只能允许十进制输入,例如:X,XXX(小数符号前只有一位,精度为 3)。
我正在使用 C# 并试试这个 ^[0-9]+(\.[0-9]{1,2})?$?
【问题讨论】:
-
为什么要在这里使用正则表达式而不是框架解决方案(
decimal.TryParse)?
我需要验证 textbox 输入并且只能允许十进制输入,例如:X,XXX(小数符号前只有一位,精度为 3)。
我正在使用 C# 并试试这个 ^[0-9]+(\.[0-9]{1,2})?$?
【问题讨论】:
decimal.TryParse)?
^[0-9]([.,][0-9]{1,3})?$
它允许:
0
1
1.2
1.02
1.003
1.030
1,2
1,23
1,234
但不是:
.1
,1
12.1
12,1
1.
1,
1.2345
1,2345
【讨论】:
还有一种替代方法,它没有 I18n 问题(允许使用 ',' 或 '.' 但不能同时使用):Decimal.TryParse。
只是尝试转换,忽略值。
bool IsDecimalFormat(string input) {
Decimal dummy;
return Decimal.TryParse(input, out dummy);
}
这比使用正则表达式要快得多,见下文。
(Decimal.TryParse的重载可用于更精细的控制。)
性能测试结果:Decimal.TryParse:0.10277ms,Regex:0.49143ms
代码(PerformanceHelper.Run 是一个助手,它运行委托以获取传递的迭代计数并返回平均值 TimeSpan。):
using System;
using System.Text.RegularExpressions;
using DotNetUtils.Diagnostics;
class Program {
static private readonly string[] TestData = new string[] {
"10.0",
"10,0",
"0.1",
".1",
"Snafu",
new string('x', 10000),
new string('2', 10000),
new string('0', 10000)
};
static void Main(string[] args) {
Action parser = () => {
int n = TestData.Length;
int count = 0;
for (int i = 0; i < n; ++i) {
decimal dummy;
count += Decimal.TryParse(TestData[i], out dummy) ? 1 : 0;
}
};
Regex decimalRegex = new Regex(@"^[0-9]([\.\,][0-9]{1,3})?$");
Action regex = () => {
int n = TestData.Length;
int count = 0;
for (int i = 0; i < n; ++i) {
count += decimalRegex.IsMatch(TestData[i]) ? 1 : 0;
}
};
var paserTotal = 0.0;
var regexTotal = 0.0;
var runCount = 10;
for (int run = 1; run <= runCount; ++run) {
var parserTime = PerformanceHelper.Run(10000, parser);
var regexTime = PerformanceHelper.Run(10000, regex);
Console.WriteLine("Run #{2}: Decimal.TryParse: {0}ms, Regex: {1}ms",
parserTime.TotalMilliseconds,
regexTime.TotalMilliseconds,
run);
paserTotal += parserTime.TotalMilliseconds;
regexTotal += regexTime.TotalMilliseconds;
}
Console.WriteLine("Overall averages: Decimal.TryParse: {0}ms, Regex: {1}ms",
paserTotal/runCount,
regexTotal/runCount);
}
}
【讨论】:
\d{1}(\.\d{1,3})?
Match a single digit 0..9 «\d{1}»
Exactly 1 times «{1}»
Match the regular expression below and capture its match into backreference number 1 «(\.\d{1,3})?»
Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
Match the character “.” literally «\.»
Match a single digit 0..9 «\d{1,3}»
Between one and 3 times, as many times as possible, giving back as needed (greedy) «{1,3}»
Created with RegexBuddy
比赛:
1
1.2
1.23
1.234
【讨论】:
一般来说,即无限位小数:
^-?(([1-9]\d*)|0)(.0*[1-9](0*[1-9])*)?$
【讨论】:
我刚刚发现TryParse() 有一个问题,它占数千个分隔符。 En-US 中的示例,10,36.00 是可以的。我有一个特定的场景,不应该考虑千位分隔符,因此正则表达式 \d(\.\d) 原来是最好的选择。当然必须为不同的语言环境保留十进制字符变量。
【讨论】:
NumerStyles参数的Decimal.TryParse的重载,并且不要包含NumerStyles.AllowThousands。
在 .NET 中,我建议使用当前文化上下文的小数分隔符动态构建正则表达式:
using System.Globalization;
...
NumberFormatInfo nfi = NumberFormatInfo.CurrentInfo;
Regex re = new Regex("^(?\\d+("
+ Regex.Escape(nfi.CurrencyDecimalSeparator)
+ "\\d{1,2}))$");
您可能希望通过允许 1000er 分隔符与小数分隔符相同的方式来对正则表达式进行拉皮条。
【讨论】:
当我对此争论不休时,3.5 中的 TryParse 确实有 NumberStyles:以下代码也应该在没有 Regex 的情况下忽略千位分隔符。
double.TryParse(length, NumberStyles.AllowDecimalPoint,CultureInfo.CurrentUICulture, out lengthD))
与最初提出的问题无关,但确认 TryParse() 确实是一个不错的选择。
【讨论】: