web-dev-qa-db-ja.com

文字列を小数点記号「。」に変換する最良の方法。そして、「、」鈍感な方法?

アプリケーションは、異なる文化に由来する小数を表す文字列を扱います。たとえば、「1.1」と「1,1」は同じ値です。

Decimal.TryParseは組み合わせにフラグを立てますが、必要な結果を得ることができませんでした。結局「1,1」は「11」か「0」になりました。

このような文字列を、「、」文字を「」に事前に置き換えることなく、コードの1行で10進数に変換することは可能ですか?またはNumberFormat.NumberDecimalSeparator

そのような状況にどのように対処しますか?

前もって感謝します!

20
Andrew Florko

次の可能性があります。

  1. あなたは文化を知っています
    1. コンピューターがインストールされている現在のカルチャ設定を使用する
    2. ユーザーに自分のカルチャーを設定するように決定させる->プログラムのユーザー設定
  2. あなたは文化を知りません
    1. あなたはそれについて決定しなければなりません:あなたはあなたの決定を定義して文書化しなければなりません
    2. 推測:有効な数値が得られるまで、解析を試み、解析を試み、...を試みます。
6
OlimilOops

解析時に使用する一時的なCultureInfoオブジェクトを作成できます。

// get a temporary culture (clone) to modify
var ci = CultureInfo.InvariantCulture.Clone() as CultureInfo;
ci.NumberFormat.NumberDecimalSeparator = ",";
decimal number = decimal.Parse("1,1", ci); // 1.1
33
Jeff Mercado

別の方法を見つけました。奇妙に見えますが、私にとってはうまくいきます。

したがって、ターゲットシステムのカルチャがわからず、12.33または12,33のようにどの値が得られるかがわからない場合は、次のようにすることができます。

string amount = "12.33";
// or i.e. string amount = "12,33";

var c = System.Threading.Thread.CurrentThread.CurrentCulture;
var s = c.NumberFormat.CurrencyDecimalSeparator;

amount = amount.Replace(",", s);
amount = amount.Replace(".", s);

decimal transactionAmount = Convert.ToDecimal(amount); 
11
Developer

Parseを呼び出すときは、次のように、正しいカルチャを設定する必要があります。

string s = "11,20";

decimal c1 = decimal.Parse(s, new CultureInfo("fr-FR"));
decimal c2 = decimal.Parse(s, new CultureInfo("en-AU"));

Console.WriteLine(c1);
Console.WriteLine(c2);
4
Noon Silk

以下は私の実装です、良いアイディアはありますか?

/// <summary>
/// 
/// </summary>
public static class NumberExtensions
{
    /// <summary>
    /// Convert string value to decimal ignore the culture.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns>Decimal value.</returns>
    public static decimal ToDecimal ( this string value )
    {
        decimal number;
        string tempValue = value;

        var punctuation = value.Where ( x => char.IsPunctuation ( x ) ).Distinct ( );
        int count = punctuation.Count ( );

        NumberFormatInfo format = CultureInfo.InvariantCulture.NumberFormat;
        switch ( count )
        {
            case 0:
                break;
            case 1:
                tempValue = value.Replace ( ",", "." );
                break;
            case 2:
                if ( punctuation.ElementAt ( 0 ) == '.' )
                    tempValue = value.SwapChar ( '.', ',' );
                break;
            default:
                throw new InvalidCastException ( );
        }

        number = decimal.Parse ( tempValue, format );
        return number;
    }
    /// <summary>
    /// Swaps the char.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <param name="from">From.</param>
    /// <param name="to">To.</param>
    /// <returns></returns>
    public static string SwapChar ( this string value, char from, char to )
    {
        if ( value == null )
            throw new ArgumentNullException ( "value" );

        StringBuilder builder = new StringBuilder ( );

        foreach ( var item in value )
        {
            char c = item;
            if ( c == from )
                c = to;
            else if ( c == to )
                c = from;

            builder.Append ( c );
        }
        return builder.ToString ( );
    }
}

[TestClass]
public class NumberTest
{

    /// <summary>
    /// 
    /// </summary>
    [TestMethod]
    public void Convert_To_Decimal_Test ( )
    {
        string v1 = "123.4";
        string v2 = "123,4";
        string v3 = "1,234.5";
        string v4 = "1.234,5";
        string v5 = "123";
        string v6 = "1,234,567.89";
        string v7 = "1.234.567,89";

        decimal a1 = v1.ToDecimal ( );
        decimal a2 = v2.ToDecimal ( );
        decimal a3 = v3.ToDecimal ( );
        decimal a4 = v4.ToDecimal ( );
        decimal a5 = v5.ToDecimal ( );
        decimal a6 = v6.ToDecimal ( );
        decimal a7 = v7.ToDecimal ( );

        Assert.AreEqual ( ( decimal ) 123.4, a1 );
        Assert.AreEqual ( ( decimal ) 123.4, a2 );
        Assert.AreEqual ( ( decimal ) 1234.5, a3 );
        Assert.AreEqual ( ( decimal ) 1234.5, a4 );
        Assert.AreEqual ( ( decimal ) 123, a5 );
        Assert.AreEqual ( ( decimal ) 1234567.89, a6 );
        Assert.AreEqual ( ( decimal ) 1234567.89, a7 );
    }
    /// <summary>
    /// 
    /// </summary>
    [TestMethod]
    public void Swap_Char_Test ( )
    {
        string v6 = "1,234,567.89";
        string v7 = "1.234.567,89";

        string a1 = v6.SwapChar ( ',', '.' );
        string a2 = v7.SwapChar ( ',', '.' );

        Assert.AreEqual ( "1.234.567,89", a1 );
        Assert.AreEqual ( "1,234,567.89", a2 );
    }
}
3
Andy

いいですね、それでも100%正しくありません。ケース1を使用する場合: '、'が10進数を表すと自動的に想定します。少なくとも2回以上発生するかどうかを確認する必要があります。その場合は、グループを区切る記号になります。

            case 1:
                var firstPunctuation = linq.ElementAt(0);
                var firstPunctuationOccurence = value.Where(x => x == firstPunctuation).Count();

                if (firstPunctuationOccurence == 1)
                {
                    // we assume it's a decimal separator (and not a group separator)
                    value = value.Replace(firstPunctuation.ToString(), format.NumberDecimalSeparator);
                }
                else
                {
                    // multiple occurence means that symbol is a group separator
                    value = value.Replace(firstPunctuation.ToString(), format.NumberGroupSeparator);
                }

                break;
2
ajoka

2つの可能性を表す2つのスタイルでTryParseを2回使用する
一方のみが値を返す場合はその値を使用します両方が値を返す場合は絶対値で小さい方の値を使用します。

TryParseは、間違ったスタイルを使用すると、グループ化と小数点の両方を使用する数値に対して0を返しますが、文字列にグループ化区切りがない場合(たとえば、数値が1000未満の場合)は、どちらの場合も値を返しますが、 「間違った」数は大きくなります(これも絶対的な意味で)

0