답변:
이 페이지는 모든 사용자 지정 숫자 서식 규칙의 전체 목록을 제공합니다.
http://msdn.microsoft.com/en-us/library/0c899ak8.aspx
보시다시피 서수에 대해서는 아무것도 없으므로 String.Format을 사용하여 수행 할 수 없습니다. 그러나 실제로 그렇게하는 기능을 작성하는 것은 어렵지 않습니다.
public static string AddOrdinal(int num)
{
if( num <= 0 ) return num.ToString();
switch(num % 100)
{
case 11:
case 12:
case 13:
return num + "th";
}
switch(num % 10)
{
case 1:
return num + "st";
case 2:
return num + "nd";
case 3:
return num + "rd";
default:
return num + "th";
}
}
업데이트 : 기술적으로 서수는 <= 0에 존재하지 않으므로 위 코드를 업데이트했습니다. 중복 ToString()
방법 도 제거했습니다 .
또한 국제화되지 않았습니다. 나는 다른 언어에서 서 수가 어떻게 생겼는지 전혀 모른다.
국제화를 기억하십시오!
이 솔루션은 영어로만 작동합니다. 다른 언어를 지원해야 할 경우 상황이 훨씬 복잡해집니다.
예를 들어 스페인어에서 "1st"는 세는 것이 남성적이든 여성적이든 복수형인지에 따라 "1.o", "1.a", "1.os"또는 "1.as"로 작성됩니다. !
따라서 소프트웨어가 다른 언어를 지원해야하는 경우 서수를 피하십시오.
Jesse 버전의 Stu 및 samjudson 버전 :)
숫자 <1 일 때 허용 된 답변이 잘못되었음을 표시하기위한 단위 테스트 포함
/// <summary>
/// Get the ordinal value of positive integers.
/// </summary>
/// <remarks>
/// Only works for english-based cultures.
/// Code from: http://stackoverflow.com/questions/20156/is-there-a-quick-way-to-create-ordinals-in-c/31066#31066
/// With help: http://www.wisegeek.com/what-is-an-ordinal-number.htm
/// </remarks>
/// <param name="number">The number.</param>
/// <returns>Ordinal value of positive integers, or <see cref="int.ToString"/> if less than 1.</returns>
public static string Ordinal(this int number)
{
const string TH = "th";
string s = number.ToString();
// Negative and zero have no ordinal representation
if (number < 1)
{
return s;
}
number %= 100;
if ((number >= 11) && (number <= 13))
{
return s + TH;
}
switch (number % 10)
{
case 1: return s + "st";
case 2: return s + "nd";
case 3: return s + "rd";
default: return s + TH;
}
}
[Test]
public void Ordinal_ReturnsExpectedResults()
{
Assert.AreEqual("-1", (1-2).Ordinal());
Assert.AreEqual("0", 0.Ordinal());
Assert.AreEqual("1st", 1.Ordinal());
Assert.AreEqual("2nd", 2.Ordinal());
Assert.AreEqual("3rd", 3.Ordinal());
Assert.AreEqual("4th", 4.Ordinal());
Assert.AreEqual("5th", 5.Ordinal());
Assert.AreEqual("6th", 6.Ordinal());
Assert.AreEqual("7th", 7.Ordinal());
Assert.AreEqual("8th", 8.Ordinal());
Assert.AreEqual("9th", 9.Ordinal());
Assert.AreEqual("10th", 10.Ordinal());
Assert.AreEqual("11th", 11.Ordinal());
Assert.AreEqual("12th", 12.Ordinal());
Assert.AreEqual("13th", 13.Ordinal());
Assert.AreEqual("14th", 14.Ordinal());
Assert.AreEqual("20th", 20.Ordinal());
Assert.AreEqual("21st", 21.Ordinal());
Assert.AreEqual("22nd", 22.Ordinal());
Assert.AreEqual("23rd", 23.Ordinal());
Assert.AreEqual("24th", 24.Ordinal());
Assert.AreEqual("100th", 100.Ordinal());
Assert.AreEqual("101st", 101.Ordinal());
Assert.AreEqual("102nd", 102.Ordinal());
Assert.AreEqual("103rd", 103.Ordinal());
Assert.AreEqual("104th", 104.Ordinal());
Assert.AreEqual("110th", 110.Ordinal());
Assert.AreEqual("111th", 111.Ordinal());
Assert.AreEqual("112th", 112.Ordinal());
Assert.AreEqual("113th", 113.Ordinal());
Assert.AreEqual("114th", 114.Ordinal());
Assert.AreEqual("120th", 120.Ordinal());
Assert.AreEqual("121st", 121.Ordinal());
Assert.AreEqual("122nd", 122.Ordinal());
Assert.AreEqual("123rd", 123.Ordinal());
Assert.AreEqual("124th", 124.Ordinal());
}
간단하고 깨끗하며 빠릅니다.
private static string GetOrdinalSuffix(int num)
{
if (num.ToString().EndsWith("11")) return "th";
if (num.ToString().EndsWith("12")) return "th";
if (num.ToString().EndsWith("13")) return "th";
if (num.ToString().EndsWith("1")) return "st";
if (num.ToString().EndsWith("2")) return "nd";
if (num.ToString().EndsWith("3")) return "rd";
return "th";
}
또는 확장 방법으로 더 나은 방법
public static class IntegerExtensions
{
public static string DisplayWithSuffix(this int num)
{
if (num.ToString().EndsWith("11")) return num.ToString() + "th";
if (num.ToString().EndsWith("12")) return num.ToString() + "th";
if (num.ToString().EndsWith("13")) return num.ToString() + "th";
if (num.ToString().EndsWith("1")) return num.ToString() + "st";
if (num.ToString().EndsWith("2")) return num.ToString() + "nd";
if (num.ToString().EndsWith("3")) return num.ToString() + "rd";
return num.ToString() + "th";
}
}
이제 전화 만하면됩니다
int a = 1;
a.DisplayWithSuffix();
또는 심지어 직접
1.DisplayWithSuffix();
당신은 자신의 롤을해야합니다. 내 머리 꼭대기에서 :
public static string Ordinal(this int number)
{
var work = number.ToString();
if ((number % 100) == 11 || (number % 100) == 12 || (number % 100) == 13)
return work + "th";
switch (number % 10)
{
case 1: work += "st"; break;
case 2: work += "nd"; break;
case 3: work += "rd"; break;
default: work += "th"; break;
}
return work;
}
그런 다음 할 수 있습니다
Console.WriteLine(432.Ordinal());
11/12/13 예외에 대해 편집되었습니다. 나는 내 머리 꼭대기에서 말했다 :-)
1011로 편집 됨-다른 사람들은 이미이 문제를 해결했습니다. 다른 사람들 이이 잘못된 버전을 잡지 않도록하십시오.
나는 Stu 와 samjudson 의 솔루션 모두에서 요소를 좋아 했으며 사용 가능한 콤보라고 생각하는 요소로 함께 작동했습니다.
public static string Ordinal(this int number)
{
const string TH = "th";
var s = number.ToString();
number %= 100;
if ((number >= 11) && (number <= 13))
{
return s + TH;
}
switch (number % 10)
{
case 1:
return s + "st";
case 2:
return s + "nd";
case 3:
return s + "rd";
default:
return s + TH;
}
}
아직 벤치마킹하지는 않았지만 모든 조건부 사례를 피함으로써 더 나은 성능을 얻을 수 있어야합니다.
이것은 자바이지만 C #으로의 포트는 간단합니다.
public class NumberUtil {
final static String[] ORDINAL_SUFFIXES = {
"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
};
public static String ordinalSuffix(int value) {
int n = Math.abs(value);
int lastTwoDigits = n % 100;
int lastDigit = n % 10;
int index = (lastTwoDigits >= 11 && lastTwoDigits <= 13) ? 0 : lastDigit;
return ORDINAL_SUFFIXES[index];
}
public static String toOrdinal(int n) {
return new StringBuffer().append(n).append(ordinalSuffix(n)).toString();
}
}
엄격한 루프에서 많은 서수를 생성하는 경우 조건부 감소 및 배열 조회를 사용하면 성능이 향상됩니다. 그러나 나는 이것이 case statement 솔루션만큼 읽을 수 없다는 것을 인정합니다.
Ryan의 솔루션과 비슷하지만 더 기본적 인 경우 평범한 배열을 사용하고 하루를 사용하여 올바른 서수를 찾습니다.
private string[] ordinals = new string[] {"","st","nd","rd","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","th","st","nd","rd","th","th","th","th","th","th","th","st" };
DateTime D = DateTime.Now;
String date = "Today's day is: "+ D.Day.ToString() + ordinals[D.Day];
필요하지는 않았지만 여러 언어 지원을 원한다면 다차원 배열을 사용할 수 있다고 가정합니다.
Uni 시절부터 기억할 수있는 것으로부터이 방법은 서버의 노력이 거의 필요하지 않습니다.
samjudson 님의 답변에 대한 "중복 부족"버전을 요청했습니다 ...
public static string AddOrdinal(int number)
{
if (number <= 0) return number.ToString();
string GetIndicator(int num)
{
switch (num % 100)
{
case 11:
case 12:
case 13:
return "th";
}
switch (num % 10)
{
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
return number + GetIndicator(number);
}
public static
로 표시하고보다 니모닉 이름 (예 : "OrdinalSuffix")으로 이름을 바꿉니다. 발신자는 다른 형식 (예 : 쉼표)으로 숫자 부분을 원할 수 있습니다.
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";
}
편집 : YM_Industries가 의견에서 지적한 것처럼 samjudson의 답변 은 1000 개가 넘는 숫자에서 작동하지만 nickf의 의견은 사라진 것 같습니다. 그리고 내가 본 문제가 무엇인지 기억할 수 없습니다. 비교 타이밍을 위해이 답변을 여기에 남겨 두십시오.
이들의 끔찍한 많은으로, 숫자> 999 일을하지 않는 nickf이 코멘트 (지금 실종 EDIT)에서 지적했다.
다음은 수정 된 버전의 samjudson 의 승인 된 답변 을 기반으로하는 버전입니다 .
public static String GetOrdinal(int i)
{
String res = "";
if (i > 0)
{
int j = (i - ((i / 100) * 100));
if ((j == 11) || (j == 12) || (j == 13))
res = "th";
else
{
int k = i % 10;
if (k == 1)
res = "st";
else if (k == 2)
res = "nd";
else if (k == 3)
res = "rd";
else
res = "th";
}
}
return i.ToString() + res;
}
또한 문자열 조작을 사용한 Shahzad Qureshi 의 대답 은 잘 작동하지만 성능 저하가 있습니다. 이것들을 많이 생성하기 위해 LINQPad 예제 프로그램은 문자열 버전을이 정수보다 6-7 배 느리게 만듭니다 (많은 주목해야 할 것이지만).
LINQPad 예제 :
void Main()
{
"Examples:".Dump();
foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 10000013 })
Stuff.GetOrdinal(i).Dump();
String s;
System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
for(int iter = 0; iter < 100000; iter++)
foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
s = Stuff.GetOrdinal(i);
"Integer manipulation".Dump();
sw.Elapsed.Dump();
sw.Restart();
for(int iter = 0; iter < 100000; iter++)
foreach(int i in new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22, 113, 122, 201, 202, 211, 212, 2013, 1000003, 1000013 })
s = (i.ToString() + Stuff.GetOrdinalSuffix(i));
"String manipulation".Dump();
sw.Elapsed.Dump();
}
public class Stuff
{
// Use integer manipulation
public static String GetOrdinal(int i)
{
String res = "";
if (i > 0)
{
int j = (i - ((i / 100) * 100));
if ((j == 11) || (j == 12) || (j == 13))
res = "th";
else
{
int k = i % 10;
if (k == 1)
res = "st";
else if (k == 2)
res = "nd";
else if (k == 3)
res = "rd";
else
res = "th";
}
}
return i.ToString() + res;
}
// Use string manipulation
public static string GetOrdinalSuffix(int num)
{
if (num.ToString().EndsWith("11")) return "th";
if (num.ToString().EndsWith("12")) return "th";
if (num.ToString().EndsWith("13")) return "th";
if (num.ToString().EndsWith("1")) return "st";
if (num.ToString().EndsWith("2")) return "nd";
if (num.ToString().EndsWith("3")) return "rd";
return "th";
}
}
FWIW, MS-SQL의 경우이식이 작업을 수행합니다. 첫 번째 WHEN ( WHEN num % 100 IN (11, 12, 13) THEN 'th'
)을 목록의 첫 번째로 유지하십시오 .
CASE
WHEN num % 100 IN (11, 12, 13) THEN 'th' -- must be tried first
WHEN num % 10 = 1 THEN 'st'
WHEN num % 10 = 2 THEN 'nd'
WHEN num % 10 = 3 THEN 'rd'
ELSE 'th'
END AS Ordinal
Excel의 경우 :
=MID("thstndrdth",MIN(9,2*RIGHT(A1)*(MOD(A1-11,100)>2)+1),2)
(FALSE = 0)으로 (MOD(A1-11,100)>2)
끝나는 것을 제외한 모든 숫자에 대해 표현식 은 TRUE (1)입니다 11,12,13
. 그래서 2 * RIGHT(A1) * (MOD(A1-11,100)>2) +1)
그렇지 않으면 11/12/13 1로 끝나는 :
1 ~ 3로 평가
5-2,
3 ~ 7
기타 : 9
-와가에서 선택되는 2 개 문자 필요한 "thstndrdth"
그 위치에서 시작.
이것을 상당히 직접 SQL로 직접 변환하려면 몇 가지 테스트 값이 도움이되었습니다.
DECLARE @n as int
SET @n=13
SELECT SubString( 'thstndrdth'
, (SELECT MIN(value) FROM
(SELECT 9 as value UNION
SELECT 1+ (2* (ABS(@n) % 10) * CASE WHEN ((ABS(@n)+89) % 100)>2 THEN 1 ELSE 0 END)
) AS Mins
)
, 2
)
이것은 구현이며 dart
언어에 따라 수정 될 수 있습니다.
String getOrdinalSuffix(int num){
if (num.toString().endsWith("11")) return "th";
if (num.toString().endsWith("12")) return "th";
if (num.toString().endsWith("13")) return "th";
if (num.toString().endsWith("1")) return "st";
if (num.toString().endsWith("2")) return "nd";
if (num.toString().endsWith("3")) return "rd";
return "th";
}
여기에는 좋은 대답이 많이 있지만 다른 것에 대한 여지가 있다고 생각합니다. 이번에는 패턴 일치를 기반으로합니다. 다른 것이 아니라면 적어도 논쟁의 여지가 있습니다.
public static string Ordinals1(this int number)
{
switch (number)
{
case int p when p % 100 == 11:
case int q when q % 100 == 12:
case int r when r % 100 == 13:
return $"{number}th";
case int p when p % 10 == 1:
return $"{number}st";
case int p when p % 10 == 2:
return $"{number}nd";
case int p when p % 10 == 3:
return $"{number}rd";
default:
return $"{number}th";
}
}
이 솔루션이 특별한 이유는 무엇입니까? 다른 다양한 솔루션에 대한 성능 고려 사항을 추가하고 있다는 사실 만
솔직히 나는이 특정 시나리오 (실제로 수백만의 서 수가 필요한)에 대해 성능이 중요하다고 의심하지만 적어도 비교를 고려해야 할 부분이 있습니다 ...
참조를위한 100 만 항목 (밀링은 기계 사양에 따라 다를 수 있음)
패턴 일치 및 나누기 (이 답변)
~ 622ms
패턴 일치 및 문자열 (이 답변)
~ 1967ms
두 개의 스위치와 구분 (허용 된 답변)
~ 637ms
하나의 스위치와 부서로 (또 다른 대답)
~ 725ms
void Main()
{
var timer = new Stopwatch();
var numbers = Enumerable.Range(1, 1000000).ToList();
// 1
timer.Reset();
timer.Start();
var results1 = numbers.Select(p => p.Ordinals1()).ToList();
timer.Stop();
timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and divisions");
// 2
timer.Reset();
timer.Start();
var results2 = numbers.Select(p => p.Ordinals2()).ToList();
timer.Stop();
timer.Elapsed.TotalMilliseconds.Dump("with pattern matching and strings");
// 3
timer.Reset();
timer.Start();
var results3 = numbers.Select(p => p.Ordinals3()).ToList();
timer.Stop();
timer.Elapsed.TotalMilliseconds.Dump("with two switches and divisons");
// 4
timer.Reset();
timer.Start();
var results4 = numbers.Select(p => p.Ordinals4()).ToList();
timer.Stop();
timer.Elapsed.TotalMilliseconds.Dump("with one switche and divisons");
}
public static class Extensions
{
public static string Ordinals1(this int number)
{
switch (number)
{
case int p when p % 100 == 11:
case int q when q % 100 == 12:
case int r when r % 100 == 13:
return $"{number}th";
case int p when p % 10 == 1:
return $"{number}st";
case int p when p % 10 == 2:
return $"{number}nd";
case int p when p % 10 == 3:
return $"{number}rd";
default:
return $"{number}th";
}
}
public static string Ordinals2(this int number)
{
var text = number.ToString();
switch (text)
{
case string p when p.EndsWith("11"):
return $"{number}th";
case string p when p.EndsWith("12"):
return $"{number}th";
case string p when p.EndsWith("13"):
return $"{number}th";
case string p when p.EndsWith("1"):
return $"{number}st";
case string p when p.EndsWith("2"):
return $"{number}nd";
case string p when p.EndsWith("3"):
return $"{number}rd";
default:
return $"{number}th";
}
}
public static string Ordinals3(this int number)
{
switch (number % 100)
{
case 11:
case 12:
case 13:
return $"{number}th";
}
switch (number % 10)
{
case 1:
return $"{number}st";
case 2:
return $"{number}nd";
case 3:
return $"{number}rd";
default:
return $"{number}th";
}
}
public static string Ordinals4(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}th";
case 2:
return $"{number}nd";
case 3:
return $"{number}rd";
default:
return $"{number}th";
}
}
}
또 다른 one-liner이지만 정규 표현식 결과를 배열로만 색인화하여 비교하지 않습니다.
public static string GetOrdinalSuffix(int input)
{
return new []{"th", "st", "nd", "rd"}[Convert.ToInt32("0" + Regex.Match(input.ToString(), "(?<!1)[1-3]$").Value)];
}
PowerShell 버전을 더 단축 할 수 있습니다.
function ord($num) { return ('th','st','nd','rd')[[int]($num -match '(?<!1)[1-3]$') * $matches[0]] }
다음은 DateTime Extension 클래스입니다. 복사, 붙여 넣기 및 즐기기
공개 정적 클래스 DateTimeExtensions {
public static string ToStringWithOrdinal(this DateTime d)
{
var result = "";
bool bReturn = false;
switch (d.Day % 100)
{
case 11:
case 12:
case 13:
result = d.ToString("dd'th' MMMM yyyy");
bReturn = true;
break;
}
if (!bReturn)
{
switch (d.Day % 10)
{
case 1:
result = d.ToString("dd'st' MMMM yyyy");
break;
case 2:
result = d.ToString("dd'nd' MMMM yyyy");
break;
case 3:
result = d.ToString("dd'rd' MMMM yyyy");
break;
default:
result = d.ToString("dd'th' MMMM yyyy");
break;
}
}
if (result.StartsWith("0")) result = result.Substring(1);
return result;
}
}
결과 :
2014 년 10 월 9 일