2009-08-30 55 views
172

Tôi muốn phân tích cú pháp một chuỗi như "3.5" thành gấp đôi. Tuy nhiên,Làm cách nào để phân tích cú pháp một chuỗi có dấu thập phân thành dấu hai chấm?

double.Parse("3.5") 

mang lại 35 và

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

ném một FormatException.

Bây giờ miền địa phương của máy tính của tôi được đặt thành tiếng Đức, trong đó dấu phẩy được sử dụng làm dấu phân cách thập phân. Nó có thể phải làm điều gì đó với điều đó và double.Parse() mong đợi "3,5" làm đầu vào, nhưng tôi không chắc chắn.

Làm cách nào để phân tích chuỗi chứa số thập phân có thể hoặc không được định dạng như được chỉ định trong ngôn ngữ hiện tại của tôi?

+0

Dấu phẩy thập phân chắc chắn sẽ ảnh hưởng đến đầu ra. – ChrisF

+9

Đừng quên phương thức double.TryParse() nếu thích hợp với hoàn cảnh của bạn. –

Trả lời

334
double.Parse("3.5", CultureInfo.InvariantCulture) 
+0

Tôi thích sử dụng lớp 'XmlConvert' ... bạn có bất kỳ ý tưởng nào cho dù điều này tốt hơn, tệ hơn và/hoặc khác với việc sử dụng' CultureInfo.InvariantCulture'? – ChrisW

+1

Vâng, 'XmlConvert' không thực sự có ý định được sử dụng để phân tích một giá trị gấp đôi trong mã. Tôi thích sử dụng 'double.Parse' hoặc' Convert.ToDouble' làm cho ý định của tôi trở nên rõ ràng hơn. –

+4

Điều này có nghĩa là doulble.Parse sử dụng văn hóa mặc định có thể không chứa dấu chấm làm dấu thập phân ?? –

15

Bí quyết là sử dụng văn hóa bất biến, để phân tích cú pháp dấu chấm trong tất cả các nền văn hóa.

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

Không hoạt động trong mọi tình huống. 1.234.567,89 – JanW

+0

@JanW trong đó điều này không hoạt động? – BendEg

60

tôi thường là sử dụng một chức năng đa văn hóa để phân tích đầu vào sử dụng, chủ yếu là bởi vì nếu ai đó đang sử dụng để Numpad và đang sử dụng một nền văn hóa có sử dụng dấu phẩy để tách các số thập phân, người đó sẽ sử dụng các điểm numpad thay vì dấu phẩy.

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; 
} 

Hãy coi chừng, @nikie nhận xét là đúng. Để bảo vệ tôi, tôi sử dụng chức năng này trong một môi trường được kiểm soát, nơi tôi biết rằng văn hóa có thể là en-US, en-CA hoặc fr-CA. Tôi sử dụng hàm này vì trong tiếng Pháp, chúng tôi sử dụng dấu phẩy làm dấu tách thập phân, nhưng bất kỳ ai từng làm việc trong tài chính sẽ luôn sử dụng dấu tách thập phân trên numpad, nhưng đây là một điểm, không phải dấu phẩy. Vì vậy, ngay cả trong văn hóa fr-CA, tôi cần phải phân tích cú pháp số sẽ có một điểm như dấu tách thập phân.

+13

Tôi không chắc đó là một ý tưởng hay. Bạn không thể phân tích cú pháp gấp đôi nếu bạn không biết văn hóa: Ở Đức, các giá trị kép có thể chứa '.'s, nhưng chúng được coi là hàng nghìn dấu phân tách ở đó. Vì vậy, trong trường hợp của Legate, GetDouble ("3.5") sẽ trả về 35 trong một miền địa phương của Đức và 3,5 trong một môi trường en-us. – Niki

+0

Không, Pierre Alain là đúng, vì nó chính xác như được viết. Nếu văn hóa của bạn nói dấu phân tách "dấu chấm là một nghìn", thì "3.5" được xem là "35" và nó tốt. Tuy nhiên, nếu bạn văn hóa như không có quy tắc cho "dấu chấm", thì ký tự được phân tích cú pháp dưới dạng dấu thập phân và nó cũng hoạt động. Tôi đã tránh được thử văn hóa enUS, nhưng đó là một lựa chọn cá nhân. – xryl669

+0

Nếu bạn sử dụng numpad trong văn hóa bằng dấu phẩy, phím dấu chấm sẽ được đặt dấu phẩy. – CrazyBaran

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

Thay thế dấu phẩy bằng một điểm trước khi phân tích cú pháp. Hữu ích ở các quốc gia có dấu phẩy làm dấu phân cách thập phân. Hãy suy nghĩ về việc hạn chế đầu vào của người dùng (nếu cần) vào một dấu phẩy hoặc điểm.

+1

Câu trả lời chính xác hơn nhiều so với câu trả lời có +133 phiếu bầu ... Nó cho phép sống trên cả hai hệ thống bằng "," hoặc "." dấu phân cách thập phân ... – Badiboy

+0

@Badiboy bạn có thể giải thích những gì sai với câu trả lời này không? Khi tôi hiểu InvariantCulture luôn luôn có '.' dưới dạng dấu phân cách thập phân. Vì vậy, nó sẽ làm việc cho cả hai hệ thống. – AlexP11223

+0

@ Alex11223 Bạn nói đúng. Đó là lý do tại sao tôi nói rằng câu trả lời này là tốt hơn sau đó phổ biến hơn một. Tái bút: Thân thiện khi nói mã này cũng sẽ thất bại nếu bạn có dấu "," là LIST SEPARATOR (tức là 1,234,560,01), nhưng tôi không biết cách giải quyết điều này chút nào. :) – Badiboy

1

Mã sau thực hiện công việc trong bất kỳ trường hợp nào. Đó là một chút phân tích cú pháp.

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); 
} 
+0

1.234.567.890 sẽ trả về 1234567.890 –

+0

Tôi chưa thử, nhưng nếu bạn thực thi ứng dụng trong các SOs văn hóa khác nhau, mã này sẽ không tạo ra mẹo, tôi nghĩ:/ –

1

Tôi nghĩ 100% chính xác chuyển đổi là không thể, nếu giá trị xuất phát từ một đầu vào người dùng. ví dụ. nếu giá trị là 123.456, nó có thể là một nhóm hoặc nó có thể là một dấu thập phân. Nếu bạn thực sự cần 100% bạn phải mô tả định dạng của bạn và ném một ngoại lệ nếu nó không đúng.

Nhưng tôi đã cải thiện mã của JanW, vì vậy chúng tôi sẽ tiến xa hơn một chút so với 100%. Ý tưởng đằng sau là, nếu dấu phân cách cuối cùng là một groupSeperator, thì đây sẽ là một kiểu số nguyên, lớn hơn gấp đôi.

Mã được thêm vào trong đầu tiên nếu của GetDouble.

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; 
} 
0

tôi cải thiện mã của @JanW cũng ...

tôi cần nó để kết quả định dạng từ dụng cụ y tế, và họ cũng có thể gửi "> 1000", "23.3e02", "350E- 02 ", và" 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
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

Điều này sẽ không hoạt động đối với các quốc gia sử dụng dấu phân cách nghìn. – noggin182

0

Thay vì phải xác định một miền địa phương trong tất cả các phân tích, tôi thích để cài đặt một ứng dụng bản địa rộng, mặc dù nếu định dạng chuỗi là không đồng nhất giữa các ứng dụng, đây không thể làm việc.

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

Xác định điều này khi bắt đầu ứng dụng của bạn sẽ làm cho tất cả các phân tích kép mong đợi dấu phẩy làm dấu phân cách thập phân. Bạn có thể thiết lập một miền địa phương thích hợp để dấu tách thập phân và hàng nghìn khớp với các chuỗi bạn đang phân tích.

6

Hãy xem, mọi câu trả lời ở trên đề xuất viết một chuỗi thay thế bằng một chuỗi liên tục chỉ có thể sai. Tại sao? Vì bạn không tôn trọng cài đặt vùng của Windows! Windows đảm bảo người dùng có quyền tự do đặt bất kỳ ký tự phân cách nào mà anh ta muốn. S/Anh ta có thể mở bảng điều khiển, đi vào bảng điều khiển khu vực, nhấp vào nâng cao và thay đổi nhân vật bất cứ lúc nào. Ngay cả khi chương trình của bạn chạy. Hãy nghĩ về điều này. Một giải pháp tốt phải nhận thức được điều này.

Vì vậy, trước tiên, bạn sẽ phải tự hỏi mình, số này đến từ đâu, bạn muốn phân tích cú pháp. Nếu nó đến từ đầu vào trong .NET Framework thì không có vấn đề gì, bởi vì nó sẽ có cùng định dạng. Nhưng có lẽ nó đến từ bên ngoài, có lẽ từ một máy chủ bên ngoài, có thể từ một DB cũ chỉ hỗ trợ các thuộc tính chuỗi. Ở đó, quản trị viên db phải đưa ra một quy tắc trong đó định dạng các số được lưu trữ. Ví dụ: nếu bạn biết rằng đó sẽ là một DB Hoa Kỳ có định dạng Hoa Kỳ, bạn có thể sử dụng đoạn mã sau:

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

Điều này sẽ hoạt động tốt ở mọi nơi trên thế giới. Và xin vui lòng không sử dụng 'Convert.ToXxxx'. Lớp 'Chuyển đổi' được coi là cơ sở cho các chuyển đổi theo bất kỳ hướng nào. Bên cạnh đó: Bạn cũng có thể sử dụng cơ chế tương tự cho DateTimes.

+0

Đồng ý! Cố gắng thực hiện thủ công các tính năng Văn hóa cuối cùng sẽ dẫn đến một trường hợp bạn không mong đợi và một nhức đầu lớn. Hãy xử lý nó đúng cách. – Khalos

+1

một vấn đề lớn là khi người dùng đang sử dụng dấu phân tách thập phân không được coi là dấu tách thập phân cho cài đặt văn hóa của nó – EdmundYeung99

0

Thật khó mà không chỉ định những gì thập phân tách để tìm kiếm, nhưng nếu bạn làm thế, đây là những gì tôi đang sử dụng:

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", ','); 
    } 

này nên làm việc với bất kỳ nền văn hóa. Chính xác là không phân tích cú pháp các chuỗi có nhiều dấu tách thập phân, không giống như các thay thế thay thế cho trao đổi.

-2

Phần dưới đây kém hiệu quả hơn, nhưng tôi sử dụng logic này. Điều này chỉ hợp lệ nếu bạn có hai chữ số sau dấu thập phân.

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)); 
-1

Nhân số và chia cho số bạn đã nhân với trước đó.

Ví dụ,

perc = double.Parse("3.555)*1000; 
result = perc/1000 
13

tôi không thể viết chú thích, vì vậy tôi viết ở đây:

double.Parse ("3,5", CultureInfo.InvariantCulture) không phải là một ý tưởng tốt, bởi vì ở Canada chúng tôi viết 3,5 thay vì 3,5 và chức năng này cho chúng ta 35 kết quả là.

Tôi đã thử nghiệm cả trên máy tính của tôi:

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

Đây là một cách chính xác rằng Pierre-Alain Vigeant nêu

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; 
} 
+0

Re: * "... vì ở Canada chúng tôi viết 3,5 thay vì 3,5" * Bạn có chắc chắn về điều đó không? Theo * [Dấu thập phân] (https://en.wikipedia.org/wiki/Decimal_mark#Countries_using_Arabic_numerals_with_decimal_point): "Các quốc gia có dấu chấm". "Được sử dụng làm dấu thập phân bao gồm ... Canada (khi sử dụng tiếng Anh)" * . Không phải là nó nhiều hơn về việc sử dụng một phiên bản tiếng Pháp của Windows? –

+0

Baybe vì phiên bản tiếng Pháp. Trong montreal chúng tôi viết 3,5 không 3,5 –

+0

Vì vậy, theo logic của bạn, luôn luôn chỉ có 1 trong số họ trở về sự thật? – batmaci

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

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

double _dbl = double.Parse(_pos); 
-2

Tôi nghĩ rằng đó là câu trả lời tốt nhất :

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; 
} 
+0

Giải thích mã của bạn và cung cấp thêm chi tiết sẽ hữu ích. –

+0

Điều gì cần giải thích ở đây? Mọi thứ đều có trong nhận xét – Endorphinex

Các vấn đề liên quan