2013-09-02 1 views
5

На самом деле, у меня есть несколько систем, которые могут генерировать числовые данные, и они хранятся на каком-либо веб-сервере в текстовых файлах. Некоторые из систем используют десятичную точку в качестве разделителя фракций, некоторые из систем используют десятичную запятую как таковые.Межрегиональный десятичный/двойной синтаксический анализ

Приложения (жирный клиент, .net 2.0) также могут быть запущены на любых системах.

Таким образом, после некоторого спотыкаясь я сделал это: (http://pastebin.com/vhLXABDD)

public static bool HasDecimalComma; 
    public static bool HasDecimalPeriod; 

    public static double GetNumber(string NumberString) 
    { 
     if (!HasDecimalComma && !HasDecimalPeriod) 
     { 
      string s = string.Format("{0:0.0}", 123.123); 
      if (s.Contains('.')) 
      { 
       HasDecimalPeriod = true; 
      } 
      else if (s.Contains(',')) 
      { 
       HasDecimalComma = true; 
      } 
      else 
      { 
       throw new SystemException(string.Format("strange number format '{0}'", s)); 
      } 
     } 
     if (HasDecimalComma) 
     { 
      return double.Parse(NumberString.Replace('.', ',')); 
     } 
     if (HasDecimalPeriod) 
     { 
      return double.Parse(NumberString.Replace(',', '.')); 
     } 
     throw new ArgumentException(string.Format("can't parse '{0}'", NumberString)); 
    } 

вы могли бы предложить какие-либо лучше, более элегантный способ?

EDIT:

Я извиняюсь за не упоминать это раньше, и так как ваши ответы постных в этом направлении - я не могу хранить культуру формирования с номерами, я могу попробовать только «обнаружить» это.

+0

Использование 'CultureInfo'? – Leri

+0

Можете ли вы сохранить генерирующую культуру с числовой строкой? Это облегчит любую потребность в таком изгибе. – SWeko

+0

Знаете ли вы, что будет сгенерировать номера? например, до набора опций? Я не уверен, как ваша система будет разбираться с разбором «1,000.12» (на самом деле я знаю, это сломается). Если у вас ограниченное количество возможных форматов, вы можете просто попробовать каждый из них, если у них нет возможности двусмысленности. – Chris

ответ

0

Если вы не можете изменить сайт вызова, и вы гарантируете, что никакой другой вид разделителя не будет присутствовать рядом с разделителем десятичных знаков, вы можете более или менее использовать ваш метод. Я хотел бы предложить

  1. Мoving в HasDecimalComma и HasDecimalPeriod к телу метода - глобальное состояние абсолютно не нужно в этом случае.
  2. Использование TryParse вместо Parse, поскольку ожидается, что числа будут потенциально неисправными.
  3. Исключительно указывая культуру InvariantCulture (она имеет десятичный период).
  4. Разрешение числа, не имеющего ни запятой, ни периодом, так как «3» является числом с плавающей запятой в конце концов.

Так что-то вдоль этих линий:

///comment the method assumptions here 
///otherwise the method might seem wrong 
public static double GetNumber(string numberString) 
{ 
    bool hasDecimalComma = numberString.Contains(','); 
    if (hasDecimalComma) 
    numberString = numberString.Replace(',', '.') 
    double result; 
    bool success = double.TryParse(numberString, 
         NumberStyles.Float, 
         CultureInfo.InvariantCulture, 
         out result); 
    if (success) 
    return result; 
    else 
    throw new ArgumentException(
          string.Format("can't parse '{0}'", numberString)); 
} 

(Старого ответ, хорошо в принципе, на практике невозможно)

Я хотел бы предложить хранение генераторных культур вдоль струны, и затем используя его для вызова метода по этим линиям (с использованием double.TryParse):

public static double GetNumber(string numberString, CultureInfo culture) 
{ 
    double result; 
    bool success = double.TryParse(numberString, 
          NumberStyles.Float | NumberStyles.AllowThousands, 
          culture, 
          out result); 
    if (success) 
     return result; 
    else 
     throw new ArgumentException(
           string.Format("can't parse '{0}'", numberString)); 
} 
+0

Изменен ответ в свете этого. – SWeko

2

Попробуйте с этим:

static double GetDouble(string s) 
    { 
     double d; 

     var formatinfo = new NumberFormatInfo(); 

     formatinfo.NumberDecimalSeparator = "."; 

     if (double.TryParse(s, NumberStyles.Float, formatinfo, out d)) 
     { 
      return d; 
     } 

     formatinfo.NumberDecimalSeparator = ","; 

     if (double.TryParse(s, NumberStyles.Float, formatinfo, out d)) 
     { 
      return d; 
     } 

     throw new SystemException(string.Format("strange number format '{0}'", s)); 
    } 
+0

Это может не работать в случае, если '.' используется как разделитель тысяч. – Chris

+1

По-видимому, это не проблема, как было выяснено в комментариях (на вопрос), так что это выглядит хорошо. – Chris

0

Просто используйте текущую культуру и правильные флаги форматирования номера. Вы должны проверить его во всех культурах, которые потенциально хранятся в вашей базе данных. Лучше: конвертировать числа в CultureInfo.Invariant культуры, прежде чем даже хранить их в базе данных. Или: Сохраните также идентификатор культуры при сохранении номера.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Globalization; 

namespace ConsoleApplication2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      const string toTest = "1.006,30"; 

      float number; 
      if (float.TryParse(toTest, NumberStyles.AllowDecimalPoint | NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.CurrentCulture, out number)) 
       Console.WriteLine("Success: {0}", number); 
      else 
       Console.WriteLine("Failure: strange number format"); 

      Console.WriteLine("Press any key to finish"); 
      Console.ReadKey(); 
     } 
    } 
} 
0

Если только некоторые из ваших клиентов использует персидский (fa, fa-IR тег) в качестве своей культуры, я думаю, вы хорошо идти:

// apparently, Persians use '/' as the decimal separator 
var slash = CultureInfo 
    .GetCultures(CultureTypes.AllCultures) 
    .Where(c => 
     c.NumberFormat.NumberDecimalSeparator != "," && 
     c.NumberFormat.NumberDecimalSeparator != ".") 
    .ToList();