Мне интересно, есть ли метод или строка формата, которую мне не хватает в .NET, чтобы преобразовать следующее:
1 to 1st
2 to 2nd
3 to 3rd
4 to 4th
11 to 11th
101 to 101st
111 to 111th
У Эта ссылка есть плохой пример основного принципа, задействованного в написании вашей собственной функции, но мне более любопытно, есть ли встроенная емкость, которой мне не хватает.
Решение
Ответ Скотта Хансельмана принят, потому что он дает прямой ответ на вопрос.
Однако для решения см. этот отличный ответ.
Здесь был дан довольно элегантный ответ: stackoverflow.com/questions/20156/ordinals-in-c#





Нет, в библиотеке базовых классов .NET нет встроенных возможностей.
Это запоздалый комментарий, но есть ли планы добавить эту возможность в .NET BCL для форматирования дат?
Как насчет сейчас, в 2019 году?
Это функция намного проще, чем вы думаете. Хотя для этого может уже существовать функция .NET, следующая функция (написанная на PHP) выполняет свою работу. Перенести это не должно быть слишком сложно.
function ordinal($num) {
$ones = $num % 10;
$tens = floor($num / 10) % 10;
if ($tens == 1) {
$suff = "th";
} else {
switch ($ones) {
case 1 : $suff = "st"; break;
case 2 : $suff = "nd"; break;
case 3 : $suff = "rd"; break;
default : $suff = "th";
}
}
return $num . $suff;
}
А как насчет локализации?
Локализация будет означать, что вам придется создавать отдельные функции для каждого языка. В немецком языке вы можете просто добавить «тер», но «1тер» «2тер» «3тер» выглядит очень плохо, даже несмотря на то, что это грамматически правильно. На французском немного лучше, но универсального способа для каждого языка нет.
@Michael Stum: Я не слишком знаком со всеми международными порядковыми форматами, но будет ли достаточно string.Format (resString, number)? Или в некоторых языках числа не сочетаются с порядковыми (пре / суффикс) индексами?
@MichaelStum: На самом деле на немецком языке нельзя просто добавить «тер». Рассмотрим "Heute ist der 1te Januar" (сегодня 1 января). Или «Klicken Sie den 5ten Button» (нажмите 5-ю кнопку). Назовем лишь два из десятков случаев. Вы должны учитывать правильное сгибание (англ. Изгиб) для каждого использования.
Сочетание числа и такого суффикса необычно для немецкого языка. Вы либо напишите это как «1». или "эрстер" / "эрсте". Последний обычно используется в текстах, и его редко нужно генерировать автоматически.
Разве это не подведет для чисел от 10 до 20?
@dannio Нет, есть условие для $tens
Это уже было рассмотрено, но я не знаю, как на это ссылаться Вот фрагмент кода:
public static string Ordinal(this int number)
{
var ones = number % 10;
var tens = Math.Floor (number / 10f) % 10;
if (tens == 1)
{
return number + "th";
}
switch (ones)
{
case 1: return number + "st";
case 2: return number + "nd";
case 3: return number + "rd";
default: return number + "th";
}
}
К вашему сведению: это метод расширения. Если ваша версия .NET меньше 3.5, просто удалите это ключевое слово
[РЕДАКТИРОВАТЬ]: Спасибо, что указали, что это неверно, это то, что вы получаете за копирование / вставку кода :)
Не работает. 1011% 10 == 1. 1011-е неверно.
Мне нравится, как вы объявляете переменную ones и никогда ее не используете.
@MattMitchell В вашем примере это будет 10110-й, а не 1011-й
@EOLeary не уверен, что вы говорите, но я думаю, что редактирование Маршалла послужило моему примеру.
Зачем использовать float вместо простых целых чисел? Я бы использовал (number / 10) % 10.
Нет необходимости использовать Math.Floor; деление отбрасывает десятичные дроби, если оба числа - int. Следует читать как var tens = number / 10 % 10;.
Я думаю, что порядковый суффикс трудно получить ... вам в основном нужно написать функцию, которая использует переключатель для проверки чисел и добавления суффикса.
У языка нет причин предоставлять это внутренне, особенно когда он зависит от локали.
Вы можете сделать немного лучше, чем эта ссылка, когда дело доходит до количества кода для написания, но вам нужно написать функцию для этого ...
Учитывая все строки локализации валюты и т. д., Добавление порядкового суффикса кажется немного натянутым.
@nickf: Вот функция PHP на C#:
public static string Ordinal(int number)
{
string suffix = String.Empty;
int ones = number % 10;
int tens = (int)Math.Floor(number / 10M) % 10;
if (tens == 1)
{
suffix = "th";
}
else
{
switch (ones)
{
case 1:
suffix = "st";
break;
case 2:
suffix = "nd";
break;
case 3:
suffix = "rd";
break;
default:
suffix = "th";
break;
}
}
return String.Format("{0}{1}", number, suffix);
}
Ха, спасибо, вот-вот отправлю код, который я написал. Думаю, ваш в любом случае превосходит мой с битом String.Format.
1) Почему перевод в десятичный? Простой (number / 10) % 10 делает свое дело. 2) Почему вы инициализируете suffix значением, которое никогда не будет использоваться?
@CodesInChaos: без преобразования в десятичный формат вы получите ошибку компилятора: The call is ambiguous between the following methods or properties: 'System.Math.Floor(decimal)' and 'System.Math.Floor(double)'. Инициализация suffix на String.Empty в основном является привычкой, но также помогает избежать случайных ошибок Use of unassigned local variable 'suffix'.
@ScottDorman 1) Только если вы оставите вызов Floor, что бессмысленно для целых чисел. Целочисленное деление просто обрезается до нуля, нет необходимости приводить к десятичному виду или использовать Floor. (number / 10) % 10 попроще и работает. 2) Эти ошибки возникают, если вы пропустили путь кода. Ошибка компилятора говорит вам исправить эту ошибку вместо того, чтобы молча возвращать бесполезное значение.
else if (choice=='q')
{
qtr++;
switch (qtr)
{
case(2): strcpy(qtrs,"nd");break;
case(3):
{
strcpy(qtrs,"rd");
cout<<"End of First Half!!!";
cout<<" hteam "<<"["<<hteam<<"] "<<hs;
cout<<" vteam "<<" ["<<vteam;
cout<<"] ";
cout<<vs;dwn=1;yd=10;
if (beginp=='H') team='V';
else team='H';
break;
}
case(4): strcpy(qtrs,"th");break;
Ну давай же. что ты пишешь? Это не имеет ничего общего с моим вопросом.
Вот версия функции Microsoft SQL Server:
CREATE FUNCTION [Internal].[GetNumberAsOrdinalString]
(
@num int
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE @Suffix nvarchar(2);
DECLARE @Ones int;
DECLARE @Tens int;
SET @Ones = @num % 10;
SET @Tens = FLOOR(@num / 10) % 10;
IF @Tens = 1
BEGIN
SET @Suffix = 'th';
END
ELSE
BEGIN
SET @Suffix =
CASE @Ones
WHEN 1 THEN 'st'
WHEN 2 THEN 'nd'
WHEN 3 THEN 'rd'
ELSE 'th'
END
END
RETURN CONVERT(nvarchar(max), @num) + @Suffix;
END
Я только что написал эту функцию почти дословно! Различия: master db, cast вместо convert, и я использую немного другой отступ. Думаю, великие умы ...
+1 - Просто была потребность в версии SQL - спасло меня, написав одну
Превосходно, но только если мы получаем данные из SQL. Но в этом случае я форматирую переменную DateTime .net. Но эта функция будет безмерно полезной.
Я знаю, что это не ответ на вопрос OP, но поскольку я счел полезным вывести функцию SQL Server из этого потока, вот эквивалент Delphi (Pascal):
function OrdinalNumberSuffix(const ANumber: integer): string;
begin
Result := IntToStr(ANumber);
if (((Abs(ANumber) div 10) mod 10) = 1) then // Tens = 1
Result := Result + 'th'
else
case(Abs(ANumber) mod 10) of
1: Result := Result + 'st';
2: Result := Result + 'nd';
3: Result := Result + 'rd';
else
Result := Result + 'th';
end;
end;
Имеет ли смысл ..., -1, 0?
Просто, чисто, быстро
private static string GetOrdinalSuffix(int num)
{
string number = num.ToString();
if (number.EndsWith("11")) return "th";
if (number.EndsWith("12")) return "th";
if (number.EndsWith("13")) return "th";
if (number.EndsWith("1")) return "st";
if (number.EndsWith("2")) return "nd";
if (number.EndsWith("3")) return "rd";
return "th";
}
Или еще лучше, как метод расширения
public static class IntegerExtensions
{
public static string DisplayWithSuffix(this int num)
{
string number = num.ToString();
if (number.EndsWith("11")) return number + "th";
if (number.EndsWith("12")) return number + "th";
if (number.EndsWith("13")) return number + "th";
if (number.EndsWith("1")) return number + "st";
if (number.EndsWith("2")) return number + "nd";
if (number.EndsWith("3")) return number + "rd";
return number + "th";
}
}
Теперь ты можешь просто вызвать
int a = 1;
a.DisplayWithSuffix();
или даже прямо как
1.DisplayWithSuffix();
Наверное, самый изящный ответ здесь.
Я думаю это самый чистый способ сделать это
Несомненно лучший ответ. На самом деле это полагается на число как на текст, а не на сложную математическую формулу. Именно так бы это понял сам человеческий мозг, и это идеально.
Это хороший ответ, но если метод называется DisplayWithSuffix, то возвращаемый ответ должен включать число, чтобы 1 возвращал «1st», а не просто «st».
Хорошее замечание, HitLikeAHammer. Я настрою код, чтобы отразить это.
Вероятно, вы захотите поместить num.ToString() в переменную. Не уверен, что кто-то посчитал бы этот ответ простым, понятным или быстрым, если честно.
@Rudey Точно, -1.
Другой аромат:
/// <summary>
/// Extension methods for numbers
/// </summary>
public static class NumericExtensions
{
/// <summary>
/// Adds the ordinal indicator to an integer
/// </summary>
/// <param name = "number">The number</param>
/// <returns>The formatted number</returns>
public static string ToOrdinalString(this int number)
{
// Numbers in the teens always end with "th"
if ((number % 100 > 10 && number % 100 < 20))
return number + "th";
else
{
// Check remainder
switch(number % 10)
{
case 1:
return number + "st";
case 2:
return number + "nd";
case 3:
return number + "rd";
default:
return number + "th";
}
}
}
}
Собственно хороший ответ. Но он общий, то есть не относится к датам месяца. Я имел в виду только свидания. Таким образом, значение выше 100 может быть неприменимо.
public static string OrdinalSuffix(int ordinal)
{
//Because negatives won't work with modular division as expected:
var abs = Math.Abs(ordinal);
var lastdigit = abs % 10;
return
//Catch 60% of cases (to infinity) in the first conditional:
lastdigit > 3 || lastdigit == 0 || (abs % 100) - lastdigit == 10 ? "th"
: lastdigit == 1 ? "st"
: lastdigit == 2 ? "nd"
: "rd";
}
Они называются порядковыми числами (1-е, 2-е и т. д.) В отличие от количественных чисел (1,2,3 и т. д.), К вашему сведению.