小数点を含む文字列をdoubleに解析するにはどうすればよいですか?


231

文字列"3.5"をdoubleのように解析したい。しかしながら、

double.Parse("3.5") 

35と

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint) 

をスローしFormatExceptionます。

これで、私のコンピューターのロケールはドイツ語に設定されました。コンマが小数点として使用されます。double.Parse()それで何かをし"3,5"、入力として期待する必要があるかもしれませんが、私にはわかりません。

現在のロケールで指定されているとおりにフォーマットされているかどうかにかかわらず、10進数を含む文字列を解析するにはどうすればよいですか?


小数点のコンマは確かに出力に影響します。
ChrisF

12
状況に応じて、double.TryParse()メソッドを忘れないでください。
カイルガネット2009

回答:


414
double.Parse("3.5", CultureInfo.InvariantCulture)

私はXmlConvertクラスを使用したい...これが使用するよりも良いか、悪いか、および/または異なるかどうか、何か考えはありますCultureInfo.InvariantCultureか?
ChrisW 2009

1
まあ、XmlConvertコードで単一のdouble値を解析するために実際に使用することは意図されていません。私は使用することを好むdouble.ParseConvert.ToDouble、それが私の意図を明らかにします。
Mehrdad Afshari、

4
これは、doulble.Parseが小数点としてドットを含まない可能性があるデフォルトのカルチャを使用することを意味しますか?
アーメドは

3
12345678.12345678を変換すると、12345678.123457も変換されます
PUG

4
私には
うまくいき

75

私は通常、マルチカルチャ関数を使用してユーザー入力を解析します。これは、数字パッドに慣れているユーザーが小数点記号としてコンマを使用するカルチャを使用している場合、そのユーザーはカンマではなくテンキーのポイントを使用するためです。

public static double GetDouble(string value, double defaultValue)
{
    double result;

    //Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        //Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        //Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }

    return result;
}

ただし、@ nikieのコメントは真実です。私の弁護のために、私は文化がen-US、en-CAまたはfr-CAのいずれかである可能性があることを知っている制御された環境でこの関数を使用します。フランス語ではカンマを小数点記号として使用するため、この関数を使用しますが、これまで金融業界で働いていた人は常にテンキーの小数点記号を使用しますが、これはポイントであり、コンマではありません。したがって、fr-CAカルチャでも、小数点を小数点とする数値を解析する必要があります。


18
それが良い考えかどうかはわかりません。文化がわからない場合、doubleを確実に解析することはできません。ドイツでは、double値に「。」が含まれている場合がありますが、そこでは1000の区切り文字と見なされます。したがって、Legateの場合、GetDouble( "3.5")はドイツ語のロケールでは35を返し、en-us環境では3.5を返します。
ニキ、

いいえ、ピエールアランは正確に書かれているとおりです。あなたの文化が「ドットは千である」と言っているなら、「3.5」は「35」と見なされ、それは良いことです。ただし、「ドット」のルールなしで文化を構成する場合、文字は小数点として解析され、機能します。enUSカルチャーを試すことは避けたでしょうが、それは個人的な選択です。
xryl669 2013

カンマを含むカルチャでテンキーを使用する場合、ドットキーはカンマに設定されます。
CrazyBaran 2017

テンキーの小数点記号は、キーボードレイアウトに依存します(地域設定ではなく、少なくともWindows 7で)(私はハンガリー語を使用してテキスト、電子メール...、en-USを使用してコードを記述しているため、ドットのどちらでもかまいません。またはカンマ。小数点の区切り記号を「、」から「。」に、リスト区切り記号を「;」から「、」に変更したカスタマイズされた地域設定も使用します。ご存知のように、CSVを発生させます...
Steven Spark

25

コメントを書くことができなかったので、ここに書きます:

double.Parse( "3.5"、CultureInfo.InvariantCulture)は良い考えではありません。カナダでは3.5ではなく3,5と書くため、この関数は結果として35を返すからです。

私は自分のコンピューターで両方をテストしました:

double.Parse("3.5", CultureInfo.InvariantCulture) --> 3.5 OK
double.Parse("3,5", CultureInfo.InvariantCulture) --> 35 not OK

これはピエール・アラン・ビジャンが述べた正しい方法です

public static double GetDouble(string value, double defaultValue)
{
    double result;

    // Try parsing in the current culture
    if (!double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, out result) &&
        // Then try in US english
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out result) &&
        // Then in neutral language
        !double.TryParse(value, System.Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, out result))
    {
        result = defaultValue;
    }
    return result;
}

1
再:「...カナダでは3.5ではなく3.5と書くから」それでよろしいですか?小数点によると:「小数点としてドット "。"が使用されている国には...カナダ(英語を使用する場合)」があります。それはフランス語版のWindowsを使用することではありませんか?
Peter Mortensen

フランス語版のためベイブ。モントリオールでは3.5ではなく3.5と書きます
Malus Jan

だからあなたの論理によれば、常にそのうちの1つだけがtrueを返しますか?
batmaci 2017年


まだエラーがあります。GetDouble( "10 ,,,,,,,, 0"、0.0)のような入力文字列の場合。言及した関数が戻る100
Krivers

21
Double.Parse("3,5".Replace(',', '.'), CultureInfo.InvariantCulture)

解析する前に、コンマをポイントに置き換えます。小数点記号としてカンマを使用する国で役立ちます。(必要に応じて)ユーザー入力を1つのコンマまたはポイントに制限することを検討してください。


1
+133票を持つものよりもはるかに正解です。「、」または「。」を使用して、両方のシステムで実行できます。小数点区切り...
Badiboy

@Badiboyこの答えのどこが悪いのか説明してくれませんか?私が理解しているように、InvariantCultureには常に「。」があります。小数点記号として。したがって、両方のシステムで機能するはずです。
Alex P.

@ Alex11223そうです。そのため、この回答の方が人気のある回答よりも優れていると言いました。PS:このコードを友好的に話すと、リストセパレーター( "1,234,560.01")として "、"がある場合も失敗しますが、これを解決する方法はまったくわかりません。:)
Badiboy

4
一部のcultureinfoでは、は千単位の区切り文字であり、使用できるため、これは良い答えではありません。これをドットに置き換えると、いくつかのドットができて解析が失敗します:Double.Parse((12345.67).ToString( "N"、new CultureInfo( "en"))。Replace( '、'、 ')。 ')、CultureInfo.InvariantCulture)(12345.67)( "N"、新しいのCultureInfo( "EN")を).ToString交換(ので。'、 '' ') "12.345.67"としてフォーマットされます。
codingdave

1,234.56-> 1.234.56はパーサーではありません。もう1つのアイデアは、数値に「。」が含まれているかどうかを確認することです。そして、 '、'を '、'を空の文字列に置き換え、 '、'コンマのみが表示されている場合は '。'に置き換えます。
GDocal

16

トリックは、すべてのカルチャーでドットを解析するために、不変のカルチャーを使用することです。

double.Parse("3.5", System.Globalization.NumberStyles.AllowDecimalPoint, System.Globalization.NumberFormatInfo.InvariantInfo);

11

見てください、定数文字列による文字列置換の記述を提案する上記のすべての答えは間違っている可能性があります。どうして?Windowsの地域設定を尊重しないからです!Windowsは、ユーザーが自由に区切り文字を設定できることを保証します。S /彼は、コントロールパネルを開いて、領域パネルに移動し、詳細をクリックして、いつでもキャラクターを変更できます。プログラムの実行中でも。これについて考えてください。良い解決策はこれを認識している必要があります。

したがって、最初に、この番号がどこから来ているのか、解析することを自問する必要があります。.NET Frameworkの入力からのものである場合は、同じ形式であるため問題ありません。しかし、それはおそらく外部から、おそらく外部サーバーから、おそらく文字列プロパティのみをサポートする古いDBからのものでした。そこで、db管理者は、数値を格納する形式の規則を指定する必要があります。たとえば、US形式のUS DBであることがわかっている場合は、次のコードを使用できます。

CultureInfo usCulture = new CultureInfo("en-US");
NumberFormatInfo dbNumberFormat = usCulture.NumberFormat;
decimal number = decimal.Parse(db.numberString, dbNumberFormat);

これは世界中のどこでも問題なく動作します。また、「Convert.ToXxxx」は使用しないでください。'Convert'クラスは、あらゆる方向の変換のベースとしてのみ考えられています。その他:DateTimesにも同様のメカニズムを使用できます。


同意しました!カルチャー機能を手動で実装しようとすると、最終的には予期しないケースが発生し、大きな頭痛の種になります。.NETで適切に処理します。
Khalos

2
大きな問題は、ユーザーが
カルチャ

3
string testString1 = "2,457";
string testString2 = "2.457";    
double testNum = 0.5;
char decimalSepparator;
decimalSepparator = testNum.ToString()[1];

Console.WriteLine(double.Parse(testString1.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));
Console.WriteLine(double.Parse(testString2.Replace('.', decimalSepparator).Replace(',', decimalSepparator)));

2

このトピックに関する私の2セントは、一般的な二重変換メソッドを提供しようとしています。

private static double ParseDouble(object value)
{
    double result;

    string doubleAsString = value.ToString();
    IEnumerable<char> doubleAsCharList = doubleAsString.ToList();

    if (doubleAsCharList.Where(ch => ch == '.' || ch == ',').Count() <= 1)
    {
        double.TryParse(doubleAsString.Replace(',', '.'),
            System.Globalization.NumberStyles.Any,
            CultureInfo.InvariantCulture,
            out result);
    }
    else
    {
        if (doubleAsCharList.Where(ch => ch == '.').Count() <= 1
            && doubleAsCharList.Where(ch => ch == ',').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(",", string.Empty),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else if (doubleAsCharList.Where(ch => ch == ',').Count() <= 1
            && doubleAsCharList.Where(ch => ch == '.').Count() > 1)
        {
            double.TryParse(doubleAsString.Replace(".", string.Empty).Replace(',', '.'),
                System.Globalization.NumberStyles.Any,
                CultureInfo.InvariantCulture,
                out result);
        }
        else
        {
            throw new ParsingException($"Error parsing {doubleAsString} as double, try removing thousand separators (if any)");
        }
    }

    return result;
}

期待どおりに動作します:

  • 1.1
  • 1,1
  • 1,000,000,000
  • 1.000.000.000
  • 1,000,000,000.99
  • 1.000.000.000,99
  • 5,000,111.3
  • 5.000.111,3
  • 0.99,000,111,88
  • 0,99.000.111.88

デフォルトの変換は実装されていないため1.3,14、を解析しようとして失敗し1,3.14たり、同様のケースが発生したりします。


1
1000を意図した「1,000」は失敗します。
Defkon1、19年

1

次のコードは、どのようなシナリオでも機能します。少し解析しています。

List<string> inputs = new List<string>()
{
    "1.234.567,89",
    "1 234 567,89",
    "1 234 567.89",
    "1,234,567.89",
    "123456789",
    "1234567,89",
    "1234567.89",
};
string output;

foreach (string input in inputs)
{
    // Unify string (no spaces, only .)
    output = input.Trim().Replace(" ", "").Replace(",", ".");

    // Split it on points
    string[] split = output.Split('.');

    if (split.Count() > 1)
    {
        // Take all parts except last
        output = string.Join("", split.Take(split.Count()-1).ToArray());

        // Combine token parts with last part
        output = string.Format("{0}.{1}", output, split.Last());
    }

    // Parse double invariant
    double d = double.Parse(output, CultureInfo.InvariantCulture);
    Console.WriteLine(d);
}

2
1.234.567.890は1234567.890を返します
Dan Vogel

私は試していませんが、異なる文化のSOでアプリを実行する場合、このコードはうまくいかないと思います:/
Dani bISHOP

1

値がユーザー入力からのものである場合、100%正しい変換は不可能だと思います。たとえば、値が123.456の場合、グループ化することも、小数点にすることもできます。本当に100%必要な場合は、フォーマットを記述し、正しくない場合は例外をスローする必要があります。

しかし、JanWのコードを改善したので、100%に少し進んでいます。背後にある考え方は、最後のセパレータがgroupSeperatorである場合、これはdoubleではなく整数型になるということです。

追加されたコードはGetDoubleの最初のifにあります

void Main()
{
    List<string> inputs = new List<string>() {
        "1.234.567,89",
        "1 234 567,89",
        "1 234 567.89",
        "1,234,567.89",
        "1234567,89",
        "1234567.89",
        "123456789",
        "123.456.789",
        "123,456,789,"
    };

    foreach(string input in inputs) {
        Console.WriteLine(GetDouble(input,0d));
    }

}

public static double GetDouble(string value, double defaultValue) {
    double result;
    string output;

    // Check if last seperator==groupSeperator
    string groupSep = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator;
    if (value.LastIndexOf(groupSep) + 4 == value.Count())
    {
        bool tryParse = double.TryParse(value, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.CurrentCulture, out result);
        result = tryParse ? result : defaultValue;
    }
    else
    {
        // Unify string (no spaces, only . )
        output = value.Trim().Replace(" ", string.Empty).Replace(",", ".");

        // Split it on points
        string[] split = output.Split('.');

        if (split.Count() > 1)
        {
            // Take all parts except last
            output = string.Join(string.Empty, split.Take(split.Count()-1).ToArray());

            // Combine token parts with last part
            output = string.Format("{0}.{1}", output, split.Last());
        }
        // Parse double invariant
        result = double.Parse(output, System.Globalization.CultureInfo.InvariantCulture);
    }
    return result;
}

1
        var doublePattern = @"(?<integer>[0-9]+)(?:\,|\.)(?<fraction>[0-9]+)";
        var sourceDoubleString = "03444,44426";
        var match = Regex.Match(sourceDoubleString, doublePattern);

        var doubleResult = match.Success ? double.Parse(match.Groups["integer"].Value) + (match.Groups["fraction"].Value == null ? 0 : double.Parse(match.Groups["fraction"].Value) / Math.Pow(10, match.Groups["fraction"].Value.Length)): 0;
        Console.WriteLine("Double of string '{0}' is {1}", sourceDoubleString, doubleResult);

0

すべての解析でロケールを指定する代わりに、アプリケーション全体のロケールを設定することをお勧めします。ただし、文字列形式がアプリ全体で一貫していない場合、これは機能しない可能性があります。

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("pt-PT");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("pt-PT");

これをアプリケーションの最初に定義すると、すべての二重解析で小数点の区切り文字としてカンマが期待されます。小数点と3桁ごとの区切り文字が解析する文字列に合うように、適切なロケールを設定できます。


0

検索する小数点記号を指定しないと難しいですが、指定した場合、これは私が使用しているものです。

    public static double Parse(string str, char decimalSep)
    {
        string s = GetInvariantParseString(str, decimalSep);
        return double.Parse(s, System.Globalization.CultureInfo.InvariantCulture);
    }

    public static bool TryParse(string str, char decimalSep, out double result)
    {
        // NumberStyles.Float | NumberStyles.AllowThousands got from Reflector
        return double.TryParse(GetInvariantParseString(str, decimalSep), NumberStyles.Float | NumberStyles.AllowThousands, System.Globalization.CultureInfo.InvariantCulture, out result);
    }

    private static string GetInvariantParseString(string str, char decimalSep)
    {
        str = str.Replace(" ", "");

        if (decimalSep != '.')
            str = SwapChar(str, decimalSep, '.');

        return str;
    }
    public static string SwapChar(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();
    }

    private static void ParseTestErr(string p, char p_2)
    {
        double res;
        bool b = TryParse(p, p_2, out res);
        if (b)
            throw new Exception();
    }

    private static void ParseTest(double p, string p_2, char p_3)
    {
        double d = Parse(p_2, p_3);
        if (d != p)
            throw new Exception();
    }

    static void Main(string[] args)
    {
        ParseTest(100100100.100, "100.100.100,100", ',');
        ParseTest(100100100.100, "100,100,100.100", '.');
        ParseTest(100100100100, "100.100.100.100", ',');
        ParseTest(100100100100, "100,100,100,100", '.');
        ParseTestErr("100,100,100,100", ',');
        ParseTestErr("100.100.100.100", '.');
        ParseTest(100100100100, "100 100 100 100.0", '.');
        ParseTest(100100100.100, "100 100 100.100", '.');
        ParseTest(100100100.100, "100 100 100,100", ',');
        ParseTest(100100100100, "100 100 100,100", '.');
        ParseTest(1234567.89, "1.234.567,89", ',');    
        ParseTest(1234567.89, "1 234 567,89", ',');    
        ParseTest(1234567.89, "1 234 567.89",     '.');
        ParseTest(1234567.89, "1,234,567.89",    '.');
        ParseTest(1234567.89, "1234567,89",     ',');
        ParseTest(1234567.89, "1234567.89",  '.');
        ParseTest(123456789, "123456789", '.');
        ParseTest(123456789, "123456789", ',');
        ParseTest(123456789, "123.456.789", ',');
        ParseTest(1234567890, "1.234.567.890", ',');
    }

これはどの文化でも機能するはずです。スワップの代わりに置換する実装とは異なり、複数の小数点を含む文字列を正しく解析できません。


0

@JanWのコードも改善しました...

医療機器からの結果をフォーマットするために必要です。また、 "> 1000"、 "23.3e02"、 "350E-02"、および "NEGATIVE"も送信します。

private string FormatResult(string vResult)
{
  string output;
  string input = vResult;

  // Unify string (no spaces, only .)
  output = input.Trim().Replace(" ", "").Replace(",", ".");

  // Split it on points
  string[] split = output.Split('.');

  if (split.Count() > 1)
  {
    // Take all parts except last
    output = string.Join("", split.Take(split.Count() - 1).ToArray());

    // Combine token parts with last part
    output = string.Format("{0}.{1}", output, split.Last());
  }
  string sfirst = output.Substring(0, 1);

  try
  {
    if (sfirst == "<" || sfirst == ">")
    {
      output = output.Replace(sfirst, "");
      double res = Double.Parse(output);
      return String.Format("{1}{0:0.####}", res, sfirst);
    }
    else
    {
      double res = Double.Parse(output);
      return String.Format("{0:0.####}", res);
    }
  }
  catch
  {
    return output;
  }
}

-2
System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;

string _pos = dblstr.Replace(".",
    ci.NumberFormat.NumberDecimalSeparator).Replace(",",
        ci.NumberFormat.NumberDecimalSeparator);

double _dbl = double.Parse(_pos);

-3

私はそれが最良の答えだと思います:

public static double StringToDouble(string toDouble)
{
    toDouble = toDouble.Replace(",", "."); //Replace every comma with dot

    //Count dots in toDouble, and if there is more than one dot, throw an exception.
    //Value such as "123.123.123" can't be converted to double
    int dotCount = 0;
    foreach (char c in toDouble) if (c == '.') dotCount++; //Increments dotCount for each dot in toDouble
    if (dotCount > 1) throw new Exception(); //If in toDouble is more than one dot, it means that toCount is not a double

    string left = toDouble.Split('.')[0]; //Everything before the dot
    string right = toDouble.Split('.')[1]; //Everything after the dot

    int iLeft = int.Parse(left); //Convert strings to ints
    int iRight = int.Parse(right);

    //We must use Math.Pow() instead of ^
    double d = iLeft + (iRight * Math.Pow(10, -(right.Length)));
    return d;
}

コードを説明し、詳細を提供すると役立ちます。
チャーリーフィッシュ

ここで何を説明しますか?すべてがコメントに含まれています
Endorphinex 2016

-3

以下は効率が悪いですが、私はこのロジックを使用しています。これは、小数点以下2桁の場合にのみ有効です。

double val;

if (temp.Text.Split('.').Length > 1)
{
    val = double.Parse(temp.Text.Split('.')[0]);

    if (temp.Text.Split('.')[1].Length == 1)
        val += (0.1 * double.Parse(temp.Text.Split('.')[1]));
    else
        val += (0.01 * double.Parse(temp.Text.Split('.')[1]));
}
else
    val = double.Parse(RR(temp.Text));

-5

数値を掛けてから、前に掛けたもので割ります。

例えば、

perc = double.Parse("3.555)*1000;
result = perc/1000
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.