217

C#で数値の数を作成する簡単な方法はありますか? 例えば:

  • 1 リターン 1st
  • 2 リターン 2nd
  • 3 リターン 3 位
  • ...等

これを行うことができますString.Format()か、またはこれを行うために利用できる機能はありますか?

4

21 に答える 21

333

このページには、すべてのカスタム数値フォーマット ルールの完全なリストが表示されます。

カスタム数値フォーマット文字列

ご覧のとおり、序数については何も含まれていないため、 を使用して行うことはできません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()メソッドを削除しました。

また、これは国際化されていないことに注意してください。序数が他の言語でどのように見えるかはわかりません。

于 2008-08-21T15:03:47.973 に答える
77

国際化を忘れないでください!

ここでの解決策は英語でのみ機能します。他の言語をサポートする必要がある場合、事態はさらに複雑になります。

たとえば、スペイン語の「1st」は、数える対象が男性形、女性形、複数形のいずれであるかに応じて、「1.o」、「1.a」、「1.os」、または「1.as」と表記されます。 !

したがって、ソフトウェアが異なる言語をサポートする必要がある場合は、序数を避けるようにしてください。

于 2008-09-22T15:17:33.343 に答える
23

私のバージョンの 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());
}
于 2009-03-06T21:31:34.640 に答える
16

自分で巻く必要があります。頭のてっぺんから:

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 用に編集 -- 他の人は既にこれを修正していますが、他の人がこの間違ったバージョンを取得しないようにしたいだけです。

于 2008-08-21T14:59:10.967 に答える
13

私はStusamjudsonの両方のソリューションの要素が好きで、それらを組み合わせて、使用可能なコンボだと思います。

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;
    }
}
于 2008-08-27T19:56:15.977 に答える
9

これについてはまだベンチマークを行っていませんが、条件付きの 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 ステートメントのソリューションほど読みにくいことも認めます。

于 2008-09-21T17:52:01.517 に答える
2

私はこの拡張クラスを使用します:

public static class Int32Extensions
{
    public static string ToOrdinal(this int i)
    {
        return (i + "th")
            .Replace("1th", "1st")
            .Replace("2th", "2nd")
            .Replace("3th", "3rd");
    }
}
于 2016-09-23T16:15:29.777 に答える
1

EDIT : YM_Industries がコメントで指摘しているように、samjudson の回答は 1000 を超える数でも機能します。nickf のコメントはなくなったようで、私が見た問題が何であったか思い出せません。比較のタイミングについては、この回答をここに残してください。

nickfがコメントで指摘したように、これらの非常に多くの数が > 999 では機能しません(編集: 現在欠落しています)。

これは、 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";
        }
}
于 2014-12-16T16:59:06.723 に答える
1

ここには良い答えがたくさんありますが、別の答えの余地があると思います。今回は、パターン マッチングに基づいています。

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 ミリ秒

2つのスイッチと分割を使用(受け入れられた回答)

〜637ミリ秒

1つのスイッチと部門で(別の答え)

~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";
        }
    }
}

于 2019-10-14T14:03:47.823 に答える
0

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

エクセルの場合:

=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 に
評価さ れ ます。



"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
                )
于 2014-09-19T13:46:47.880 に答える