基于货币代码将小数格式化为货币形式

9
我有一个收费表格,其中包括金额和货币代码(USD、JPY、CAD、EUR等),我正在寻找最简单的方法来正确地格式化货币。使用我的本地区域代码(美国)并采用decimal.ToString("c")得到$0.00输出,但我想要根据代码获得正确的货币符号和小数位数。是否存在适用于此的库?当然,我可以编写一个switch语句和自定义规则,但我认为这一定已经做过了。
更新:
我已修改Jon B的示例代码如下
static IDictionary<string, string> GetCurrencyFormatStrings()
{
    var result = new Dictionary<string, string>();

    foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
    {
        try
        {
            var ri = new RegionInfo(ci.Name);
            result[ri.ISOCurrencySymbol] =
                  string.Format("{0}#,#0.{1};({0}#,#0.{1})",
                                ri.CurrencySymbol,
                                new string('0', 
                                i.NumberFormat.CurrencyDecimalDigits));
        }
        catch { }
    }

    return result;
}

这让我能够简单地使用Amount.ToString(Currency["JPY"]),格式会在本地环境中输出逗号分隔符,但自动放置正确的货币符号和小数位。如果有更简洁的方法,请告诉我,否则我将很快将Jon的答案标记为正确。

@AakashM - 实际上它是存在的。它被称为CultureInfo - Security Hound
撤回我之前的评论,我注意到RegionInfo知道一个地区货币的货币代码,所以框架确实对此有一些了解。我猜想你可能需要迭代查找,因为我没有看到任何基于货币代码的查找。 - AakashM
@Ramhound CultureInfo 告诉您如何在给定的文化中格式化类似货币的数量。它并不告诉您 JPY 的符号是 ¥。 - AakashM
1
看这里? - Black Light
3个回答

30
public static class DecimalExtension
{
    private static readonly Dictionary<string, CultureInfo> ISOCurrenciesToACultureMap =
        CultureInfo.GetCultures(CultureTypes.SpecificCultures)
            .Select(c => new {c, new RegionInfo(c.LCID).ISOCurrencySymbol})
            .GroupBy(x => x.ISOCurrencySymbol)
            .ToDictionary(g => g.Key, g => g.First().c, StringComparer.OrdinalIgnoreCase);

    public static string FormatCurrency(this decimal amount, string currencyCode)
    {
        CultureInfo culture;
        if (ISOCurrenciesToACultureMap.TryGetValue(currencyCode, out culture))
            return string.Format(culture, "{0:C}", amount);
        return amount.ToString("0.00");
    }
}

以下是使用不同货币代码调用FormatCurrency的结果:

decimal amount = 100;

amount.FormatCurrency("AUD");  //$100.00
amount.FormatCurrency("GBP");  //£100.00
amount.FormatCurrency("EUR");  //100,00 €
amount.FormatCurrency("VND");  //100,00 ?
amount.FormatCurrency("IRN");  //? 100.00

4
不错的代码片段,但最好使用 c.Name 替代 c.LCID,因为你可以获得机器特定的 CultureNotFoundExceptions - Chad Hedgcock
非常感谢 @ChadHedgcock,你让我的一天变得美好! - Chandler
这是一个非常特殊的情况,但当你需要它时,它非常有用。非常感谢jignesh和@ChadHedgcock。 - Zodman

5

通过传递 CultureInfo 来完成:

CultureInfo ci = new CultureInfo("en-GB");
amount.ToString("C", ci);

4
+0. 我认为这不会产生一个看起来合理的结果。如果两个货币值使用不同的小数分隔符(例如,美元和欧元将分别表示为$1.23和"Eur"1,23),那么输出中的数字格式化部分将无法匹配。这将导致读者根据其所在地区不同,其中一个数字看起来像是123。 - Alexei Levenkov

2

您可以创建一个字典,将ISO货币符号(USD)转换为货币符号($):

static void Main(string[] args)
{
    var symbols = GetCurrencySymbols();

    Console.WriteLine("{0}{1:0.00}", symbols["USD"], 1.5M);
    Console.WriteLine("{0}{1:0.00}", symbols["JPY"], 1.5M);

    Console.ReadLine();
}

static IDictionary<string, string> GetCurrencySymbols()
{
    var result = new Dictionary<string, string>();

    foreach (var ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
    {
        try
        {
            var ri = new RegionInfo(ci.Name);
            result[ri.ISOCurrencySymbol] = ri.CurrencySymbol;                    
        }
        catch { }
    }

    return result;
}

这是基本思路,您需要根据自己的需求进行微调。

请注意,您当然可以使用CultureInfo类将其转换为特定文化的字符串,但正如Alexei的评论中所指出的那样,这将使每个字符串略有不同(例如1.00与1,00)。您使用哪个取决于您的需求。


1
这看起来非常接近 - 并非所有货币都有2位小数(日元的最小增量为1),这也包含在区域信息中吗? - Superman
1
看起来引用 ci.NumberFormat.CurrencyDecimalDigits 就可以得到我需要的一切 - 谢谢! - Superman
1
你将会在像印度这样的国家遇到问题,该国有14种语言,多种货币符号(取决于语言),以及多种货币格式(同样取决于语言)。此外,@Superman可能不想使用印度“RegionInfo”(रु)的货币符号来表示印度卢比;我猜测他的客户/用户更喜欢看到特定文化“en-IN”(Rs.)的货币符号,而不是他们最可能读成“_squiggle_”的东西。做好这些事情很难,需要大量的文化知识。 - Nicholas Carey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接