Как изменить строковое представление NaN в С#?

Моя программа сохраняет облако точек в файл, где каждое облако точек представляет собой Point3D[,] из пространства имен System.Windows.Media.Media3D. Это показывает строку выходного файла (на португальском языке):

-112,644088741971;71,796623005014;NaN (Não é um número)

в то время как я хотел бы, чтобы это было (чтобы впоследствии правильно анализировать):

-112,644088741971;71,796623005014;NaN

Блок кода, который генерирует файл, находится здесь:

var lines = new List<string>();

for (int rows = 0; rows < malha.GetLength(0); rows++) {
    for (int cols = 0; cols < malha.GetLength(1); cols++) {

        double x = coordenadas_x[cols];
        double y = coordenadas_y[rows];
        double z;

        if ( SomeTest() ) {
            z = alglib.rbfcalc2(model, x, y);
        } else {
            z = double.NaN;
        }

        var p = new Point3D(x, y, z);
        lines.Add(p.ToString());                       

        malha[rows, cols] = p;
    }
}

File.WriteAllLines("../../../../dummydata/malha.txt", lines);

Похоже, что метод double.NaN.ToString(), вызываемый изнутри Point3D.ToString(), включает в себя это заключенное в скобки "дополнительное объяснение", которое мне совсем не нужно.

Есть ли способ изменить/переопределить этот метод, чтобы он выводил только NaN без круглых скобок?


person heltonbiker    schedule 08.03.2013    source источник
comment
Взгляните на NumberFormatInfo.NaNSymbol (раздел примечаний на msdn.microsoft.com/ en-us/library/0c899ak8.aspx)   -  person daniloquio    schedule 08.03.2013
comment
@daniloquio Как вы думаете, вы могли бы предоставить быстрый рабочий код с вашим предложением? Я почти уверен, что это то, что я искал!   -  person heltonbiker    schedule 08.03.2013
comment
отредактировал мой текущий ответ с решением. Взгляни, пожалуйста   -  person daniloquio    schedule 08.03.2013


Ответы (4)


Double.ToString() использует NumberFormatInfo.CurrentInfo для форматирования своих чисел. Это последнее свойство ссылается на CultureInfo, который в настоящее время установлен в активном потоке. По умолчанию используется текущая локаль пользователя. В данном случае это португальская культура. Чтобы избежать такого поведения, используйте перегрузку Double.ToString(IFormatProvider). В этом случае вы можете использовать CultureInfo.InvariantCulture.

Кроме того, вы можете просто переключить символ NaN, если хотите сохранить всю остальную разметку. По умолчанию информация о глобализации доступна только для чтения. Создание клона обойдет это.

System.Globalization.NumberFormatInfo numberFormatInfo = 
    (System.Globalization.NumberFormatInfo) System.Globalization.NumberFormatInfo.CurrentInfo.Clone();
numberFormatInfo.NaNSymbol = "NaN";

double num = double.NaN;
string numString = System.Number.FormatDouble(num, null, numberFormatInfo);

Чтобы установить это в текущем потоке, создайте копию текущей культуры и установите информацию о числовом формате для культуры. до .NET 4.5 есть нет способа установить его для всех потоков. После создания каждого потока вам нужно будет обеспечить правильный CultureInfo. Начиная с .NET 4.5 есть CultureInfo.DefaultThreadCurrentCulture, который определяет культуру по умолчанию для потоков в пределах AppDomain. Этот параметр учитывается только в том случае, если язык и региональные параметры потока еще не установлены (см. MSDN).

Пример для одного потока:

System.Globalization.CultureInfo myCulture =
     (System.Globalization.CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
myCulture.NumberFormat.NaNSymbol = "NaN";

System.Threading.Thread.CurrentThread.CurrentCulture = myCulture;   
string numString = double.NaN.ToString();
person Caramiriel    schedule 08.03.2013
comment
Это не приведет к желаемому результату, который использует локализованный десятичный разделитель. - person Ben Voigt; 08.03.2013
comment
В настоящее время я получаю строку NaN (não é um número). Моя цель состоит не в том, чтобы получить NaN (not a number), а в том, чтобы просто получить NaN. Как я мог получить это? - person heltonbiker; 08.03.2013
comment
Согласно комментарию Daniloquio, существует свойство NumberFormatInfo.NaNSymbol, которое получает или устанавливает строку, представляющую значение IEEE NaN (не число). Итак, мой вопрос сводился бы к тому, как я могу установить NaNSymbol культуры моего текущего потока? - person heltonbiker; 08.03.2013
comment
@heltonbiker: Вы хотите изменить это глобально для всей вашей программы или только для форматирования Point3D внутри этого цикла? - person Ben Voigt; 08.03.2013
comment
В идеале это была бы вся программа, потому что это было бы, на мой взгляд, более общим, мощным и правильным решением. Я бы предпочел иметь программу, которая создает строки так, как я хочу, чем переформатировать произвольные выходные данные. - person heltonbiker; 08.03.2013
comment
Я буду придерживаться подхода «культура для текущего потока», но для протокола: я протестировал output.Replace(System.Globalization.NumberFormatInfo.CurrentInfo.NaNSymbol, "NaN"), и он работал нормально. Хороший быстрый и грязный трюк! - person heltonbiker; 08.03.2013
comment
+1 Отредактировано с небольшими исправлениями в реализации. Я проверил это, и я могу сказать, что это работает как шарм. - person daniloquio; 08.03.2013

Просто не передавайте значения NaN в ToString.

Например (обертка в метод расширения для удобства повторного использования):

static string ToCleanString(this double val)
{
    if (double.IsNan(val)) return "NaN";
    return val.ToString();
}
person Ben Voigt    schedule 08.03.2013
comment
Я обновил свой код. На самом деле метод double.NaN.ToString() вызывается из метода Point3D.ToString()... - person heltonbiker; 08.03.2013
comment
@heltonbiker: Да, я думал об этом. И это меняет дело. Я думаю, что я предпочел бы просто заменить вызов Point3D.ToString() моим собственным методом, который преобразует и объединяет все три значения под моим контролем. - person Ben Voigt; 08.03.2013

Как насчет:

NumberFormatInfo myFormatInfo = NumberFormatInfo.InvariantInfo;

Point3D myPoint = new Point3D(1,1,double.NaN);
var pointString = myPoint.ToString(myFormatInfo);
person publicgk    schedule 08.03.2013

Прежде всего, ответ, предоставленный Caramiriel, - это решение, позволяющее представить double.NaN ЛЮБОЙ строкой, которую вы можете пожелать.

Между прочим, мне нужна строка "NaN", и вот что такое документы говорят о NumberFormatInfo.NaNSymbol:

Строка, представляющая значение IEEE NaN (не число). По умолчанию для InvariantInfo используется значение «NaN».

Затем я понял, как получить желаемую чистую строку «NaN» И избавиться от разделителя запятой, используя значение по умолчанию, предоставленное InvariantCultureInfo, добавив следующую строку сразу после создания текущего потока:

Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture;

И это сработало отлично!

person heltonbiker    schedule 08.03.2013