嗨,我想找出是否有更好的方法可以将字符串解析为涵盖各种格式的Decimal
我看到了很多使用文化来转换.
和,
的示例。但就我而言,我没有任何东西可以识别这种文化。
此显示字段是我从客户端获得的,需要提取值。
我尝试了以下操作(并非在所有情况下都适用),但想知道我们是否有最佳方法来解决此问题。
Decimal.Parse(value,NumberStyles.Currency |
NumberStyles.Number|NumberStyles.AllowThousands |
NumberStyles.AllowTrailingSign | NumberStyles.AllowCurrencySymbol)
我还尝试使用Regex删除货币符号,但无法在一种逻辑中同时转换1.8或1,8。
答案 0 :(得分:1)
唯一的方法是从符号中删除字符串并进行change。和到小数点分隔符。像这样:
public decimal UniversalConvertDecimal(string str)
{
char currentDecimalSeparator = Convert.ToChar(Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator);
str = str.Replace('.', currentDecimalSeparator);
str = str.Replace(',', currentDecimalSeparator);
StringBuilder builder = new StringBuilder(str.Length);
foreach(var ch in str)
{
if(Char.IsDigit(ch) || ch == currentDecimalSeparator)
builder.Add(ch);
}
string s = builder.ToString();
return Convert.ToDecimal(s);
}
首先,您必须从系统中获取当前的十进制分隔符。 然后,您必须更换。和,使用当前的小数点分隔符。 接下来,您将不得不从除数字或小数点分隔符之外的任何其他字符中去除字符串。最后,您可以确定Convert.ToDecimal将起作用。但是我不知道这是否是您想要实现的目标。
如果您需要某种机制来将货币保存到数据库,则有一个简单得多的解决方案。只需将这种货币转换为最少货币部分即可。例如,节省$ 100,而不是$ 1。
因此,如果您有1.99美元,将其乘以100,您将得到199美分。而且此整数可以保存到db。
答案 1 :(得分:1)
好吧,假设您始终获得有效的货币格式,并且只是文化的改变,您可以猜通过检查来确定哪个字符用作小数点,哪个字符用作千位分隔符它出现在数字的最后。然后删除所有千位分隔符并对其进行解析,就像它的区域性不变一样。
代码如下:
// Replace with your input
var numberString = "2.500,00 €";
// Regex to extract the number part from the string (supports thousands and decimal separators)
// Simple replace of all non numeric and non ',' '.' characters with nothing might suffice as well
// Depends on the input you receive
var regex = new Regex"^[^\\d-]*(-?(?:\\d|(?<=\\d)\\.(?=\\d{3}))+(?:,\\d+)?|-?(?:\\d|(?<=\\d),(?=\\d{3}))+(?:\\.\\d+)?)[^\\d]*$");
char decimalChar;
char thousandsChar;
// Get the numeric part from the string
var numberPart = regex.Match(numberString).Groups[1].Value;
// Try to guess which character is used for decimals and which is used for thousands
if (numberPart.LastIndexOf(',') > numberPart.LastIndexOf('.'))
{
decimalChar = ',';
thousandsChar = '.';
}
else
{
decimalChar = '.';
thousandsChar = ',';
}
// Remove thousands separators as they are not needed for parsing
numberPart = numberPart.Replace(thousandsChar.ToString(), string.Empty);
// Replace decimal separator with the one from InvariantCulture
// This makes sure the decimal parses successfully using InvariantCulture
numberPart = numberPart.Replace(decimalChar.ToString(),
CultureInfo.InvariantCulture.NumberFormat.CurrencyDecimalSeparator);
// Voilá
var result = decimal.Parse(numberPart, NumberStyles.AllowDecimalPoint | NumberStyles.Number, CultureInfo.InvariantCulture);
对于一个简单的十进制解析,它看起来确实有点复杂,但是我认为应该为您获得的所有输入数字或至少其中大部分输入数字做功。
如果以某种循环方式执行此操作,则可能要使用编译后的正则表达式。
答案 2 :(得分:0)
这里的问题是,在一种情况下,.
表示小数点,而在另一种情况下,则是thousnads分隔符。然后,您将,
作为小数点分隔符。显然,解析器不可能“猜测”这是什么意思,因此您唯一可以做的就是决定一些规则,以决定如何处理这种情况。
如果您可以控制UI,则最好的方法是验证用户输入,并拒绝任何无法解析的值,并提供解释说明哪种格式。
如果您无法控制UI,则第二个最佳选择是检查一些“规则”,然后设计哪种文化适合给定的输入,并尝试通过decimal.TryParse
运行给定的输入文化。
对于给定的输入,您可能具有以下规则:
input.StartsWith("$")
-> zh-CN input.StartsWith("£")
-> en-GB input.StartsWith("€")
|| input.EndsWith("€")
-> de-DE 这些可以合理地处理所有情况。
在代码中:
static void Main(string[] args)
{
string[] inputs =
{
"$1.30",
"£1.50",
"€2,50",
"2,50 €",
"2.500,00 €"
};
for (int i = 0; i < inputs.Length; i++)
{
Console.Write((i + 1).ToString() + ". ");
if (decimal.TryParse(inputs[i], NumberStyles.Currency,
GetAppropriateCulture(inputs[i]), out var parsed))
{
Console.WriteLine(parsed);
}
else
{
Console.WriteLine("Can't parse");
}
}
}
private static CultureInfo GetAppropriateCulture(string input)
{
if (input.StartsWith("$"))
return CultureInfo.CreateSpecificCulture("en-US");
if (input.StartsWith("£"))
return CultureInfo.CreateSpecificCulture("en-GB");
if (input.StartsWith("€") || input.EndsWith("€"))
return CultureInfo.CreateSpecificCulture("de-DE");
return CultureInfo.InvariantCulture;
}
输出:
- 1.30
- 1.50
- 2.50
- 2.50
- 2500.00