Как исправить приложение, которое имеет проблему с десятичным разделителем
Этот пост о C# и.Net, но некоторая информация полезна для других технологий.
Поскольку я помню, у меня были проблемы с приложениями или играми, которые вылетали из-за другого стиля анализа десятичных чисел. Это происходит очень часто, от приложений САПР, библиотек до веб-страниц. Я не уверен, что это невежество или недостаток знаний, но это действительно раздражает.
В чем проблема? Вот вики-статья об этом, но она короткая:
Вот карта, которая показывает, какой тип десятичного разделителя (десятичного знака) используется во всем мире.
Десятичные знаки:
- Период - синий
- Запятая - зеленый
- Не западно-арабские цифры - красный
- Неизвестно - серый
Большинство стран Европы и Южной Америки пишут 1 000 000,00 или 1000000,00, иногда 1.000.000,00 в противоположность "имперскому" (отмечен синим), пишут 1 000 000,00
Позвольте мне дать вам несколько из всех проблем, с которыми я столкнулся в прошлом месяце.
- Номер на веб-страницах трудно читать: давайте возьмем одно из самых просматриваемых видео YT. Это показывает мне 390159851. Числа свыше миллиона очень трудно читать, каков порядок? Это 39 млн или 390?
Пример Mirosoft XNA для Windows Phone 7: существует очень удобный класс, который анализирует XML-файл для создания анимации XNA
/// <summary> /// Loads animation setting from xml file. /// </summary> private void LoadAnimiationFromXML() { XDocument doc = XDocument.Load("Content/Textures/AnimationsDefinition.xml"); XName name = XName.Get("Definition"); var definitions = doc.Document.Descendants(name); if (animationDefinition.Attribute("Speed") != null) { animation.SetFrameInvterval(TimeSpan.FromMilliseconds( double.Parse(animationDefinition.Attribute("Speed").Value))); }
double.Parse
выбрасывает исключение, одна простая подсказка заключается в использованииXmlConvert.ToDouble();
или разобраться сInvariantCulture
,.Net приложение, которое использует CSV -файлы для хранения входного вектора в виде CSV - также выбрасывает.
Еще одно приложение.Net, в котором есть разбор внутри класса - throws.
Так как мы можем это исправить?
- Исправить ошибку в коде - возможно только с доступным исходным кодом и утомительно.
- Изменить данные (CVS, XML и т. Д.) - даже с возможными, не очень довольными изменением данных.
- Измените десятичный разделитель по умолчанию для ОС - этого не произойдет.
Есть ли другой способ решить это? Могу ли я сделать приложение инвариантным? Как запуск приложения в другой среде.
PS. Я хотел бы запустить приложение, но у меня нет кода.
5 ответов
Правильно настроен Thread.CurrentThread.CurrentCulture
и у тебя не будет таких проблем. Читайте здесь, как написать независимый от культуры код.
РЕДАКТИРОВАТЬ: Если у вас нет доступа к коду, вы можете запустить приложение под учетной записью пользователя, который имеет ожидаемый набор культур. Для быстрого доступа вы можете создать английского пользователя, немецкого пользователя, французского пользователя..
На мой взгляд, подход должен быть следующим:
- во внутреннем хранилище цифры просто цифры. так что никаких проблем не возникает.
- при разборе исходных текстовых источников вы должны анализировать их в соответствии с локалью, в которой создан источник, - скорее всего, с инвариантной культурой
- при выводе значений пользователю форматируйте их в соответствии с предпочтительным языком пользователя. в C# это означает поддержание
CurrentCulture
(неCurrentUICulture
смотрите здесь почему).
Это должно быть относительно просто и охватывать все случаи.
Это все относится к случаю, когда вы являетесь автором приложения. Для других приложений соответствующие разработчики должны сами исправить свои ошибки.
Нет другого способа решить эту проблему, просто исправьте это в коде.
Если вы захотите запустить FxCop, вы скоро научитесь всегда передавать действительный CultureInfo всем возможным ToString()
или же Parse()
метод. Честно говоря, так и должно быть, даже если вы знаете, что CultureInfo.CurrentCulture
используется по умолчанию.
Всегда передавая IFormatProvider, вы говорите другим разработчикам:
ToString(CultureInfo.CurrentCulture)
или жеParse(whatever, CultureInfo.CurrentCulture)
- это то, что конечный пользователь увидит или сможет войти, поэтому нам нужно позаботиться о его / ее культурном происхождении.ToString(CultureInfo.InvariantCulture)
или жеParse(whatever, CultureInfo.InvariantCulture)
- это то, что мы используем внутри, и это не предназначено для показа пользователю.
Теперь я знаю, что это звучит как большая работа, но это просто то, что нужно сделать. Возможно, вы просто хотели бы поблагодарить какую-то бедную душу в Microsoft, которая своим добрым намерением решила, что CurrentCulture для конечного пользователя является лучшим значением по умолчанию для провайдеров форматирования... Очевидно, что другие разработчики фреймворков, которые делали это в прошлом, были неправы и люди из МС правы, ха? Большая часть денег испарилась из-за этой глупой, неустранимой ошибки.
Я чувствую вашу боль, порт RunKeeper для Windows Phone 7 ошибся и сообщил, что в прошлом месяце я нарушил скорость света в 100 000 раз.
Если вы небрежны, вы столкнетесь с этими проблемами, но по большей части их можно избежать. Строго говоря, вы всегда должны представлять информацию в ее локализованном формате, а затем преобразовывать ее в независимое от культуры или базовое значение при сохранении. Таким образом, вы всегда можете ориентироваться на конкретные культуры.
Я полагаю, что проблема, с которой сталкиваются многие разработчики.NET, заключается в том, что они не понимают, что все стандартные методы разбора на Thread.CurrentThread.CurrentCulture
и использовать определенный формат чисел для этой культуры, и то же самое с датами и часовыми поясами.
Что-то, что я мог бы рассмотреть, это настроить цепочку ответственности, в которой сначала запрашивались варианты, специфичные для культуры, и запасной вариант, не зависящий от культуры. Я бы никогда не совмещал их, но поддержал бы независимое от культуры значение по умолчанию, которое должно работать, если вы придерживаетесь какого-то типичного соглашения (например, английские числовые форматы). Но каждый раз это может быть неправильным выбором. Обучите пользователя, если он считает, что анализ CSV-файла смешанного числового формата - это правильное решение.
Здесь нет серебряной пули.
Некоторые предложения:
Инвариантная культура может использоваться при работе с внутренними данными, которые хранятся в файле конфигурации. Таким образом, независимо от культуры, вы можете последовательно читать и записывать свои собственные внутренние данные.
Посмотреть здесь:
http://msdn.microsoft.com/en-us/library/4c5zdc6a.aspx
Другие проблемы связаны с чем-то простым, например string.ToLower().
Кажется достаточно невинным, пока вы не попытаетесь строчные буквы английского алфавита, чем не существует в современной культуре. (Например, турецкий)
В этих случаях вызов string.ToLower () должен быть заменен перегрузкой культуры, а текущая культура или инвариантная культура должны быть переданы (в зависимости от вашей ситуации).
Определенно, некоторые подводные камни, на которые стоит обратить внимание.