在c#中有一个简单的方法来创建一个数字的序数吗?例如:
1返回第1位 2返回第2 3返回第3 等
这是否可以通过String.Format()来完成,或者是否有可用的函数来完成?
在c#中有一个简单的方法来创建一个数字的序数吗?例如:
1返回第1位 2返回第2 3返回第3 等
这是否可以通过String.Format()来完成,或者是否有可用的函数来完成?
当前回答
你得自己动手了。在我的脑海中:
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编辑-其他人已经修复了这个问题,只是想确保其他人不会抓取这个错误的版本。
其他回答
Humanizer nuget包将为您提供帮助方法。免责声明,我是这个项目的贡献者。
Ordinalize将一个数字转换为一个序数字符串,用于表示在一个有序序列中的位置,例如1st, 2nd, 3rd, 4th:
1.Ordinalize() => "1st"
5.Ordinalize() => "5th"
你也可以对数字字符串调用Ordinalize函数,得到相同的结果:"21".Ordinalize() => "21st"
Ordinalize也支持两种形式的语法性别。 您可以将参数传递给Ordinalize,以指定数字应该以哪种性别输出。 可能的值为GrammaticalGender。男性,GrammaticalGender。女性和语法性别。中性:
// for Brazilian Portuguese locale
1.Ordinalize(GrammaticalGender.Masculine) => "1º"
1.Ordinalize(GrammaticalGender.Feminine) => "1ª"
1.Ordinalize(GrammaticalGender.Neuter) => "1º"
"2".Ordinalize(GrammaticalGender.Masculine) => "2º"
"2".Ordinalize(GrammaticalGender.Feminine) => "2ª"
"2".Ordinalize(GrammaticalGender.Neuter) => "2º"
显然,这只适用于某些文化。对于其他人来说,通过或不通过性别对结果没有任何影响。
此外,Ordinalize支持某些区域性应用的变体,这取决于序数在句子中的位置。 使用参数wordForm来获得一个或另一个结果。取值包括:WordForm。缩写和词形式。 你可以结合wordForm参数和性别参数,但是当它不适用时传入这个参数不会对结果产生任何影响。
// Spanish locale
1.Ordinalize(WordForm.Abbreviation) => "1.er" // As in "Vivo en el 1.er piso"
1.Ordinalize(WordForm.Normal) => "1.º" // As in "He llegado el 1º"
"3".Ordinalize(GrammaticalGender.Feminine, WordForm.Abbreviation) => "3.ª"
"3".Ordinalize(GrammaticalGender.Feminine, WordForm.Normal) => "3.ª"
"3".Ordinalize(GrammaticalGender.Masculine, WordForm.Abbreviation) => "3.er"
"3".Ordinalize(GrammaticalGender.Masculine, WordForm.Normal) => "3.º"
如果您想深入了解,请检查这些测试用例:OrdinalizeTests.cs
虽然我还没有对此进行基准测试,但通过避免所有条件case语句,您应该能够获得更好的性能。
这是java,但是移植到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语句解决方案那样可读。
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)
表达式(MOD(A1- 11100)>2)对于除以11,12,13结尾的任何数字(FALSE = 0)外的所有数字都是TRUE(1)。因此2 * RIGHT(A1) * (MOD(A1- 11100)>2) +1)对于11/12/13最终为1,否则: 1等于3 2点到5点, 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
)
虽然这里有很多很好的答案,但我想还有另一个答案的空间,这一次是基于模式匹配,如果不是为了其他任何东西,那么至少是为了有争议的可读性
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万件供参考(当然,根据机器规格,您的米粒可能会有所不同) 使用模式匹配和划分(这个答案) ~ 622毫秒 使用模式匹配和字符串(这个答案) ~ 1967毫秒 有两个开关和划分(接受答案) ~ 637毫秒 用一个开关和除法(另一个答案) ~ 725毫秒
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";
}
}
}
杰西版本的斯图和萨姆贾德森版本的我的版本:)
包含单元测试,以显示接受的答案是不正确的,当数字< 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());
}