Класс NumeralsFormatter реализует интерфейс IFormatProvider.
Позволяет с помощью форматирующих строк строить грамматически правильные выражения, содержащие числовые значения. Позволяет форматировать числа в числительные. Поддерживает только русский и английский языки.
Короче, это для детей. (С)"Наследник Хадсакера"
Возможны два типа форматирования: W — для выбора правильной формы существительного. T — для построения числительного.
Из примеров все должно быть понятно.
NumeralsFormatter formatter = new NumeralsFormatter();
string s = String.Format(formatter, format, param1, param2, ...);
Дальше примеры того, каким может быть format, и каким может быть s в зависимости от параметров.
format = "Inbox: {0} {0:W;новое,новых} {0:W;сообщение,сообщения,сообщений}";
Inbox: 1 новое сообщение
Inbox: 2 новых сообщения
Inbox: 5 новых сообщений
Сокращенная форма:
format = "Inbox: {0} {0:W;нов(ое,ых)} {0:W;сообщени(е,я,й)}";
Inbox: 1 новое сообщение
Inbox: 2 новых сообщения
Inbox: 5 новых сообщений
Для форматирования числа в числительное используется символ T:
format = "{0:T}";
Одно
Тринадцать
После форматирующего символа можно через точку с запятой указать род числительного.
От регистра форматирующего символа зависит регистр первой буквы числительного:
format = "{0:t;f}";
одна
тринадцать
Еще несколько примеров:
format = "{0:T} {0:W;час(,а,ов)}.";
Два часа.
Десять часов.
format = "{0:T;M} {0:W;час(,а,ов)} {1:t;F} {1:W;минут(а,ы,)}.";
Два часа десять минут.
Двадцать один час одна минута.
Ноль часов ноль минут.
Сумма прписью:
format = "{0:T;M} {0:W;рубл(ь,я,ей)} {1:00} коп.";
Один рубль 12 коп.
Три рубля 05 коп.
Выбор языка числительных зависит от свойства NumeralsFormatter.CultureInfo.
По умолчанию используется CultureInfo текущего потока.
Можно явно передать нужный CultureInfo в конструктор NumeralsFormatter либо изменить значения свойства.
Переходим на английский:
formatter.CultureInfo = new CultureInfo("en-US");
format = "{0:T} {0:W;dollar(,s)} and {1:t} {1:W;cent(,s)}.";
One dollar and two cents.
One hundred and twenty-three thousand, four hundred and fifty-six dollars and seven cents.
Полученную строчку не стыдно и пользователю показать, а можно и синтезатору речи передать.
Если в системе установлен MS Text To Speech API, то можно на подобии английского языка послушать, который сейчас час.
format = "{0:T} {0:W;hour(,s)} and {1:t} {1:W;minute(,s)}.";
string text = String.Format(formatter, format, DateTime.Now.Hour, DateTime.Now.Minute);
SpVoiceClass voice = new SpVoiceClass();
voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault);
Вообще-то MS Speech понимает цифры, и переводить их в текст не обязательно. Но если в языке числительные изменяются по родам (как в русском и многих других), то перевод в текст необходим.
using System;
using System.Globalization;
namespace PawnHunter.Numerals
{
/// <summary>
/// Provides formatting of numerals and countable nouns.
/// Russian and english languages are supported.
/// </summary>
/// <remarks>
/// For english numerals the Thousands system of notation is used regardless of the subculture.
/// See http://www.quinion.com/words/articles/numbers.htm for more information.
/// </remarks>public sealed class NumeralsFormatter : IFormatProvider, ICustomFormatter
{
#region Fields
private CultureInfo _cultureInfo;
#endregion Fields
#region Properties
/// <summary>
/// Gets or sets the culture for the formatter.
/// </summary>public CultureInfo CultureInfo
{
get { return _cultureInfo; }
set { _cultureInfo = value; }
}
#endregion Properties
#region Constructors
/// <summary>
/// Initializes a new instance of the NumeralsFormatter class using the current thread's culture.
/// </summary>public NumeralsFormatter()
{
_cultureInfo = CultureInfo.CurrentCulture;
}
/// <summary>
/// Initializes a new instance of the NumeralsFormatter class using the specified culture.
/// </summary>
/// <param name="cultureInfo">CultureInfo representing the culture to be used by the formatter.</param>public NumeralsFormatter(CultureInfo cultureInfo)
{
_cultureInfo = cultureInfo;
}
#endregion Constructors
#region Private methods
/// <summary>
/// Delegates numeral construction to language specific class
/// </summary>private string FormatNumeral(string[] stringFormatParts, long arg)
{
bool uppercase = Char.IsUpper(stringFormatParts[0], 0);
switch ( _cultureInfo.LCID & 0x00ff )
{
//
// Add more languages here. ;)
//case 0x09:
return English.ToString(arg, uppercase);
case 0x19:
Gender gender;
if (stringFormatParts.Length > 1)
{
switch (stringFormatParts[1].ToUpper())
{
case"F":
gender = Gender.Feminine;
break;
case"M":
gender = Gender.Masculine;
break;
case"N":
gender = Gender.Neutral;
break;
default:
throw new FormatException("Unknown gender.", new ArgumentOutOfRangeException());
}
}
else
{
gender = Gender.Neutral;
}
return Russian.ToString(arg, gender, uppercase);
default:
throw new FormatException(
String.Format("The '{0}' culture is not supported.", _cultureInfo.DisplayName),
new NotSupportedException());
}
}
/// <summary>
/// Parses formatting expression,
/// extracts and builds plural forms of a given word,
/// delegates choise of the correct form to a Countable class instance.
/// </summary>private string FormatCountable(string[] stringFormatParts, long arg)
{
string wordFormsString = stringFormatParts[stringFormatParts.Length - 1];
string[] wordForms = wordFormsString.Split('(');
string one, two, five;
if (wordForms.Length == 2)
{
// looks like "<stem>(<flexions>)" format is usedstring stem = wordForms[0];
string flectionsString = wordForms[1].Remove(wordForms[1].Length - 1, 1);
string[] flexions = flectionsString.Split(',');
one = stem + flexions[0];
two = flexions.Length > 1 ? stem + flexions[1] : one;
five = flexions.Length > 2 ? stem + flexions[2] : two;
}
else
{
// looks like "<singular>, <plural 2>, <plural 5>" format is used
wordForms = wordFormsString.Split(',');
one = wordForms[0];
two = wordForms.Length > 1 ? wordForms[1] : one;
five = wordForms.Length > 2 ? wordForms[2] : two;
}
// choose correct form for arg.
Countable countable = new Countable(one, two, five);
return countable[Math.Abs(arg)];
}
/// <summary>
/// Performs default formatting.
/// </summary>private string FormatUnknown(string format, object arg, IFormatProvider formatProvider)
{
IFormattable formattable = arg as IFormattable;
if (formattable == null)
{
return arg.ToString();
}
else
{
return formattable.ToString(format, formatProvider);
}
}
#region Helpers
/// <summary>
/// Gets a value indicating whether the type is an integral type.
/// </summary>private bool IsIntegralType(Type type)
{
if (type == typeof(int)
|| type == typeof(uint)
|| type == typeof(long)
|| type == typeof(ulong)
|| type == typeof(short)
|| type == typeof(ushort)
|| type == typeof(byte)
|| type == typeof(sbyte)
|| type == typeof(char))
{
return true;
}
else
{
return false;
}
}
#endregion Helpers
#endregion Private methods
#region IFormatProvider implementation
object IFormatProvider.GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
{
return this;
}
else
{
return CultureInfo.CurrentCulture.GetFormat(formatType);
}
}
#endregion IFormatProvider implementation
#region ICustomFormatter implementation
/// <summary>
/// Formats arg using the specified format.
/// </summary>string ICustomFormatter.Format(string format, object arg, IFormatProvider formatProvider)
{
// NumeralsFormatter processes integral types only.if (format == null || !IsIntegralType(arg.GetType()))
{
return FormatUnknown(format, arg, formatProvider);
}
// The base type for NumeralsFormatter is long,
// so check whether arg is within long's rangeif (arg.GetType() == typeof(ulong) && Convert.ToUInt64(arg) > long.MaxValue)
{
throw new FormatException("Argument is too large.",
new ArgumentOutOfRangeException("arg", arg,
String.Format("arg must be within -{0} ... {0} range.", long.MaxValue)));
}
string[] formatStringParts = format.Split(';');
// NumeralsFormatter understands only 'T'('t') and 'W'('w') format specifiers.switch (formatStringParts[0].ToUpper())
{
case"T":
return FormatNumeral(formatStringParts, Convert.ToInt64(arg));
case"W":
return FormatCountable(formatStringParts, Convert.ToInt64(arg));
default:
return FormatUnknown(format, arg, formatProvider);
}
}
#endregion ICustomFormatter
}
}
using System;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
// Microsoft Speech Object Library 5.0
// sapi.dllusing SpeechLib;
using PawnHunter.Numerals;
namespace PawnHunter
{
/// <summary>
/// Числительные и счетные существительныe.
/// Примеры использования PawnHunter.Numerals.NumeralsFormatter.
/// </summary>class NumeralsApplication
{
[STAThread]
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU");
NumeralsFormatter formatter = new NumeralsFormatter();
string format;
format = "Inbox: {0} {0:W;новое,новых} {0:W;сообщение,сообщения,сообщений}";
Console.WriteLine(String.Format(formatter, format, 1));
Console.WriteLine(String.Format(formatter, format, 2));
Console.WriteLine(String.Format(formatter, format, 5));
Console.WriteLine();
format = "Inbox: {0} {0:W;нов(ое,ых)} {0:W;сообщени(е,я,й)}";
Console.WriteLine(String.Format(formatter, format, 1));
Console.WriteLine(String.Format(formatter, format, 2));
Console.WriteLine(String.Format(formatter, format, 5));
Console.WriteLine();
format = "{0:W;Найден(а,о)} {0} {0:W;запис(ь,и,ей)}, {0:W;удовлетворяющ(ая,их)} запросу.";
Console.WriteLine(String.Format(formatter, format, 1));
Console.WriteLine(String.Format(formatter, format, 5));
Console.WriteLine();
format = "{0:T}";
Console.WriteLine(String.Format(formatter, format, 1));
Console.WriteLine(String.Format(formatter, format, 13));
Console.WriteLine();
format = "{0:t;f}";
Console.WriteLine(String.Format(formatter, format, 1));
Console.WriteLine(String.Format(formatter, format, 13));
Console.WriteLine();
format = "{0:T;M} {0:W;час(,а,ов)}.";
Console.WriteLine(String.Format(formatter, format, 2));
Console.WriteLine(String.Format(formatter, format, 10));
Console.WriteLine();
format = "{0:T;M} {0:W;час(,а,ов)} {1:t;F} {1:W;минут(а,ы,)}.";
Console.WriteLine(String.Format(formatter, format, 2, 10));
Console.WriteLine(String.Format(formatter, format, 21, 1));
Console.WriteLine(String.Format(formatter, format, 0, 0));
Console.WriteLine();
format = "{0:T;M} {0:W;рубл(ь,я,ей)} {1:00} коп.";
Console.WriteLine(String.Format(formatter, format, 1, 12));
Console.WriteLine(String.Format(formatter, format, 3, 5));
Console.WriteLine(String.Format(formatter, format, -3, -5));
Console.WriteLine();
formatter.CultureInfo = new CultureInfo("en-US");
format = "{0:T} {0:W;dollar(,s)} and {1:t} {1:W;cent(,s)}.";
Console.WriteLine(String.Format(formatter, format, 1, 2));
Console.WriteLine(String.Format(formatter, format, 123456, 7));
Console.WriteLine();
format = "{0:T} {0:W;hour(,s)} and {1:t} {1:W;minute(,s)}.";
string text = String.Format(formatter, format,
DateTime.Now.Hour, DateTime.Now.Minute);
//SpVoiceClass voice = new SpVoiceClass();
//voice.Speak(text, SpeechVoiceSpeakFlags.SVSFDefault);
Console.Write("Press ENTER to exit.");
Console.Read();
}
}
}
Дописал поддержку для украинского языка. Для подключения необходимо будет внести несколько изменений в NumeralsFormatter.cs. Здесь не привожу, так как там по тексту все понятно
Ukrainian.cs
using System;
using System.Text;
namespace PawnHunter.Numerals
{
public sealed class Ukrainian
{
#region Fields
private static Countable[] _ranks = new Countable[]
{
new Countable(" квінтильйон", " квінтильйона", " квінтильйонів", Gender.Masculine),
new Countable(" квадрильйон", " квадрильйона", " квадрильйонів", Gender.Masculine),
new Countable(" трильйон", " трильйона", " трильйонів", Gender.Masculine),
new Countable(" мільярд", " мільярда", " мільярдів", Gender.Masculine),
new Countable(" мільйон", " мільйона", " мільйонів", Gender.Masculine),
new Countable(" тисяча", " тисячі", " тисяч", Gender.Feminine),
new Countable("", "", "")
};
private static string[] _hundreds = new string[]
{
"", "сто", "двісті", "триста", "чотириста", "п'ятсот",
"шістсот", "сімсот", "вісімсот", "дев'ятсот"
};
private static string[] _tens = new string[]
{
"", "десять", "двадцять", "тридцять", "сорок", "п'ятдесят",
"шістдесят", "сімдесят", "вісімдесят", "дев'яносто"
};
private static string[] _units = new string[]
{
"нуль", "один", "два", "три", "чотири", "п'ять",
"шість", "сім", "вісім", "дев'ять", "десять", "одинадцять",
"дванадцять", "тринадцять", "чотирнадцять", "п'ятнадцять",
"шістнадцять", "сімнадцять", "вісімнадцять", "дев'ятнадцять"
};
private static string[,] _gendered = new string[,]
{
{ "один", "одна", "одне" },
{ "два", "дві", "двоє" }
};
#endregion Fields
#region Methods
public static string ToString(long number, Gender gender)
{
return ToString(number, gender, false);
}
public static string ToString(long number, Gender gender, bool uppercase)
{
if (number == 0)
{
if (uppercase)
{
return Char.ToUpper(_units[0][0]) + _units[0].Remove(0, 1);
}
else
{
return _units[0];
}
}
bool isNegative = false;
if (number < 0)
{
isNegative = true;
number = Math.Abs(number);
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < Neutral.Ranks.Length; i++ )
{
int rankValue = (int)(number / Neutral.Ranks[i]);
number %= Neutral.Ranks[i];
if (rankValue > 0)
{
int hundreds = rankValue / 100;
int tens = rankValue / 10 % 10;
int units = rankValue % 10;
if (tens == 1)
{
tens = 0;
units = rankValue % 100;
}
if (sb.Length > 0)
{
sb.Append(" ");
}
if (hundreds > 0)
{
sb.Append(_hundreds[hundreds]);
if (tens + units > 0)
{
sb.Append(" ");
}
}
if (tens > 0)
{
sb.Append(_tens[tens]);
if (units > 0)
{
sb.Append(" ");
}
}
if (units > 0)
{
// mind the genderif ( units < 3 )
{
int j = i == Neutral.Ranks.Length - 1 ? (int)gender : (int)_ranks[i].Gender;
sb.Append(_gendered[units-1, j]);
}
else
{
sb.Append(_units[units]);
}
}
sb.Append(_ranks[i][units]);
}
}
if (isNegative)
{
sb.Insert(0, "мінус ");
}
if (uppercase)
{
sb[0] = Char.ToUpper(sb[0]);
}
return sb.ToString();
}
#endregion Methods
}
}
Здравствуйте, PawnHunter, Вы писали:
PH>Здравствуйте, SiAVoL, Вы писали:
SAV>>исключительно для удобства, а то выдирать файлы из нескольких сообщения как-то не очень PH>А как?
Зайти в свой профиль, ткнуть на "файлы", а там уже сам разберешься