Отображать числа с порядковым суффиксом в PHP

149

Я хочу отображать цифры следующим образом

  • 1 как 1-й,
  • 2 как 2,
  • ...,
  • 150 как 150.

Как мне найти правильный порядковый суффикс (st, nd, rd или th) для каждого числа в моем коде?

Ковчег
источник
2
Смотрите мой ответ здесь. stackoverflow.com/questions/69262/… Этот вопрос был для .NET, но я ответил с помощью PHP-решения, поэтому оно должно помочь вам.
nickf
Единственный способ, которым я могу думать о том, чтобы сделать это, это иметь оператор if, увеличивающийся для каждого числа, которое вы можете иметь, т.е. IE, если (1), затем "st" elseif (2), затем "nd" и т. Д. И т. Д., Если (23000) then " й». Это проблема, если у вас большие числа, но вы могли бы написать программу для написания кода для вас, она могла бы зациклить все числа, печатая if для вас, чтобы скопировать + вставить в ваш код.
Том Гуллен
2
@ Том, справочная таблица может быть лучше, просто инициализируйте ее 23000 значениями и получите значение по индексу n, где n - это число, которое вы хотите получить порядковым номером.
Джон Бокер
2
Полковник Шрапнель. Вы можете быть блестящим, но не все. В любом случае спасибо за проявленный интерес к моему вопросу
ArK
@ Джон, очень умная идея, доступ к ней будет очень быстрым, так как каждый индекс представляет число, которое вы ищете.
Том Гуллен

Ответы:

283

из википедии :

$ends = array('th','st','nd','rd','th','th','th','th','th','th');
if (($number %100) >= 11 && ($number%100) <= 13)
   $abbreviation = $number. 'th';
else
   $abbreviation = $number. $ends[$number % 10];

Где $numberномер, который вы хотите написать. Работает с любым натуральным числом.

Как функция:

function ordinal($number) {
    $ends = array('th','st','nd','rd','th','th','th','th','th','th');
    if ((($number % 100) >= 11) && (($number%100) <= 13))
        return $number. 'th';
    else
        return $number. $ends[$number % 10];
}
//Example Usage
echo ordinal(100);
Iacopo
источник
Хотя поначалу это немного сложно понять, я думаю, что это лучше всего показывает, как работает система порядкового суффикса для английского языка.
Эриско
6
Мне нравится ваше решение. Кроме того, если вы предпочитаете не генерировать 0-е, измените последнюю строку, чтобы быть$abbreviation = ($number)? $number. $ends[$number % 10] : $number;
Gavin Jackson
1
@GavinJackson ваше дополнение к этому отличному решению действительно помогло мне (+1). Не могли бы вы объяснить мне, что происходит в расчете? Я хочу понять. Ура! РЕДАКТИРОВАТЬ: нашел ответ: условный оператор
Эндрю Фокс
Извините, пришлось разбить 111 голосов до 112: D Сделал это функцией в Delphi вместе с демонстрационным приложением: pastebin.com/wvmz1CHY
Джерри Додж
1
@HafezDivandari - ты уверен? Только что протестирован с 7.1.19 и, кажется, работает нормально
Iacopo
142

PHP имеет встроенную функциональность для этого . Это даже обращается с интернационализацией!

$locale = 'en_US';
$nf = new NumberFormatter($locale, NumberFormatter::ORDINAL);
echo $nf->format($number);

Обратите внимание, что эта функциональность доступна только в PHP 5.3.0 и более поздних версиях.

Джереми Кауфман
источник
15
Обратите внимание, что это также требует PECL Intl> = 1.0.0
rymo
4
Знаете ли вы, можно ли получить порядковый номер в форме слова? т.е. первый, второй, третий и т. д. вместо 1-го, 2-го, 3-го ...
its_me
@jeremy Когда я пытался использовать NumberFormatter, он всегда выдает ошибку, которая NumberFomatter file not found. Как вы обошли это?
jhnferraris
1
@Jhn вы должны установить расширениеapt-get install php5-intl
Алей
@ Эй, я вижу. У Yii есть встроенный форматер, который мы используем прямо сейчас. хе
джнферрарис
20

Это можно сделать в одной строке, используя аналогичные функции во встроенных функциях даты / времени в PHP. Я смиренно представляю:

Решение:

function ordinalSuffix( $n )
{
  return date('S',mktime(1,1,1,1,( (($n>=10)+($n>=20)+($n==0))*10 + $n%10) ));
}

Детальное объяснение:

Встроенная date()функция имеет суффиксную логику для обработки вычислений n-го числа месяца. Суффикс возвращается, когда Sзадан в строке формата:

date( 'S' , ? );

Так date()требует метки времени (для ?выше), мы передадим наше целое число в $nкачестве dayпараметра mktime()и использования фиктивных значений 1для hour, minute, secondи month:

date( 'S' , mktime( 1 , 1 , 1 , 1 , $n ) );

Это фактически терпит неудачу изящно для значений вне диапазона для дня месяца (то есть $n > 31), но мы можем добавить некоторую простую встроенную логику к пределу $nв 29:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n>=20))*10 + $n%10) ));

Единственное положительное значение( Май 2017 г. ) это не удается $n == 0, но это легко исправить, добавив 10 в этом особом случае:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n>=20)+($n==0))*10 + $n%10) ));

Обновление, май 2017

Как отмечает @donatJ, вышеупомянутый сбой превышает 100 (например, «111st»), поскольку >=20проверки всегда возвращают true. Чтобы сбросить их каждое столетие, мы добавляем фильтр для сравнения:

date( 'S', mktime( 1, 1, 1, 1, ( (($n>=10)+($n%100>=20)+($n==0))*10 + $n%10) ));

Просто оберните его в функцию для удобства и вперед!

Андрей Козак
источник
14

Вот одна строка:

$a = <yournumber>;
echo $a.substr(date('jS', mktime(0,0,0,1,($a%10==0?9:($a%100>20?$a%10:$a%100)),2000)),-2);

Вероятно, самое короткое решение. Конечно, можно обернуть функцией:

function ordinal($a) {
  // return English ordinal number
  return $a.substr(date('jS', mktime(0,0,0,1,($a%10==0?9:($a%100>20?$a%10:$a%100)),2000)),-2);
}

С уважением, Пол

EDIT1: исправление кода для 11-13.

EDIT2: исправление кода для 111, 211, ...

EDIT3: теперь это работает правильно также для кратных 10.

Павел
источник
Мне нравится такой подход, но, увы, он не работает :-( 30-е выходит как 30-е. 40-е выходит как 40-е. И т. Д.
Flukey
1
Да жаль. Когда я прочитал вопрос, который я подумал, эй, это должно быть возможно с помощью одной строки кода. И я просто напечатал это. Как вы видите из моих правок, я улучшаюсь. После третьего редактирования я думаю, что это уже сделано. По крайней мере, все цифры от 1 до 150 хорошо распечатываются на моем экране.
Пол
Хорошо смотрится до 500! (не проверял это дальше, чем это). Хорошая работа! :-)
Flukey
13

с http://www.phpro.org/examples/Ordinal-Suffix.html

<?php

/**
 *
 * @return number with ordinal suffix
 *
 * @param int $number
 *
 * @param int $ss Turn super script on/off
 *
 * @return string
 *
 */
function ordinalSuffix($number, $ss=0)
{

    /*** check for 11, 12, 13 ***/
    if ($number % 100 > 10 && $number %100 < 14)
    {
        $os = 'th';
    }
    /*** check if number is zero ***/
    elseif($number == 0)
    {
        $os = '';
    }
    else
    {
        /*** get the last digit ***/
        $last = substr($number, -1, 1);

        switch($last)
        {
            case "1":
            $os = 'st';
            break;

            case "2":
            $os = 'nd';
            break;

            case "3":
            $os = 'rd';
            break;

            default:
            $os = 'th';
        }
    }

    /*** add super script ***/
    $os = $ss==0 ? $os : '<sup>'.$os.'</sup>';

    /*** return ***/
    return $number.$os;
}
?> 
Джон Бокер
источник
9

Простой и легкий ответ будет:

$Day = 3; 
echo date("S", mktime(0, 0, 0, 0, $Day, 0));

//OUTPUT - rd
Белая лошадь
источник
Ничего не говорит о датах в вопросе tho. В любом случае я проголосовал - это быстрое и простое решение, когда вы знаете, что диапазон номеров будет <32
cronoklee
8

Я написал это для PHP4. Работает нормально и довольно экономично.

function getOrdinalSuffix($number) {
    $number = abs($number) % 100;
    $lastChar = substr($number, -1, 1);
    switch ($lastChar) {
        case '1' : return ($number == '11') ? 'th' : 'st';
        case '2' : return ($number == '12') ? 'th' : 'nd';
        case '3' : return ($number == '13') ? 'th' : 'rd'; 
    }
    return 'th';  
}
iletras
источник
Вишал Кумар, не могли бы вы расширить / объяснить / объяснить это немного подробнее? Tks!
iletras
4

вам просто нужно применить данную функцию.

function addOrdinalNumberSuffix($num) {
  if (!in_array(($num % 100),array(11,12,13))){
    switch ($num % 10) {
      // Handle 1st, 2nd, 3rd
      case 1:  return $num.'st';
      case 2:  return $num.'nd';
      case 3:  return $num.'rd';
    }
  }
  return $num.'th';
}
Чинтан Туммар
источник
3

В общем, вы можете использовать это и вызвать echo get_placing_string (100);

<?php
function get_placing_string($placing){
    $i=intval($placing%10);
    $place=substr($placing,-2); //For 11,12,13 places

    if($i==1 && $place!='11'){
        return $placing.'st';
    }
    else if($i==2 && $place!='12'){
        return $placing.'nd';
    }

    else if($i==3 && $place!='13'){
        return $placing.'rd';
    }
    return $placing.'th';
}
?>
Узаир бин Нисар
источник
2

Я сделал функцию, которая не полагается на date();функцию PHP, поскольку она не нужна, но также сделала ее настолько компактной и настолько короткой, насколько я думаю, в настоящее время возможной.

Код : (всего 121 байт)

function ordinal($i) { // PHP 5.2 and later
  return($i.(($j=abs($i)%100)>10&&$j<14?'th':(($j%=10)>0&&$j<4?['st', 'nd', 'rd'][$j-1]:'th')));
}

Более компактный код ниже.

Это работает следующим образом :

printf("The %s hour.\n",    ordinal(0));   // The 0th hour.
printf("The %s ossicle.\n", ordinal(1));   // The 1st ossicle.
printf("The %s cat.\n",     ordinal(12));  // The 12th cat.
printf("The %s item.\n",    ordinal(-23)); // The -23rd item.

Материал, чтобы знать об этой функции :

  • Он работает с отрицательными целыми числами так же, как и с положительными целыми числами, и сохраняет знак.
  • Он возвращает 11-е, 12-е, 13-е, 811-е, 812-е, 813-е и т. Д. Для 14 чисел, как и ожидалось.
  • Он не проверяет десятичные, но оставить их на месте (использование floor($i), round($i)или ceil($i)в начале заключительного возвращения заявления).
  • Вы также можете добавить format_number($i)в начале последнего оператора возврата, чтобы получить целое число через запятую (если вы отображаете тысячи, миллионы и т. Д.).
  • Вы можете просто удалить $iоператор из начала оператора return, если хотите вернуть только порядковый суффикс без ввода.

Эта функция работает начиная с версии PHP 5.2, выпущенной в ноябре 2006 года, исключительно из-за синтаксиса короткого массива. Если у вас есть версия до этого, пожалуйста, обновите, потому что вы почти на десять лет устарели! В противном случае просто замените встроенную ['st', 'nd', 'rd']временную переменную, содержащую array('st', 'nd', 'rd');.

Та же функция (без возврата входных данных), но в разобранном виде моя короткая функция для лучшего понимания:

function ordinal($i) {
  $j = abs($i); // make negatives into positives
  $j = $j%100; // modulo 100; deal only with ones and tens; 0 through 99

  if($j>10 && $j<14) // if $j is over 10, but below 14 (so we deal with 11 to 13)
    return('th'); // always return 'th' for 11th, 13th, 62912th, etc.

  $j = $j%10; // modulo 10; deal only with ones; 0 through 9

  if($j==1) // 1st, 21st, 31st, 971st
    return('st');

  if($j==2) // 2nd, 22nd, 32nd, 582nd
    return('nd'); // 

  if($j==3) // 3rd, 23rd, 33rd, 253rd
    return('rd');

  return('th'); // everything else will suffixed with 'th' including 0th
}

Обновление кода :

Вот модифицированная версия, которая на 14 байт короче (всего 107 байт):

function ordinal($i) {
  return $i.(($j=abs($i)%100)>10&&$j<14?'th':@['th','st','nd','rd'][$j%10]?:'th');
}

Или как можно короче, на 25 байт короче (всего 96 байт):

function o($i){return $i.(($j=abs($i)%100)>10&&$j<14?'th':@['th','st','nd','rd'][$j%10]?:'th');}

С этой последней функцией просто вызовите, o(121);и она будет делать то же самое, что и другие функции, которые я перечислил.

Обновление кода № 2 :

Мы с Беном работали вместе и сократили его на 38 байтов (всего 83 байта):

function o($i){return$i.@(($j=abs($i)%100)>10&&$j<14?th:[th,st,nd,rd][$j%10]?:th);}

Мы не думаем, что это может стать короче этого! Желая быть доказанным неправым, как бы то ни было. :)

Надеюсь, вам всем понравится.

nxasdf
источник
вам не нужно использовать abs()с модулем%
99 Проблемы - Синтаксис не один
Опора только по модулю все равно оставит отрицательный знак на месте. abs();удаляет отрицательный знак, который мне нужен.
nxasdf
1

Еще более короткая версия для дат в месяце (до 31) вместо использования mktime () и не требующая pecl intl:

function ordinal($n) {
    return (new DateTime('Jan '.$n))->format('jS');
}

или процедурно:

echo date_format(date_create('Jan '.$n), 'jS');

Это работает, конечно, потому что месяц по умолчанию, который я выбрал (январь), имеет 31 день.

Интересно, что если вы попробуете это с февраля (или другого месяца без 31 дня), он перезапускается до конца:

...clip...
31st
1st
2nd
3rd

так что вы можете сосчитать дни этого месяца с указателем даты tв цикле: количество дней в месяце.

Дэн Дарт
источник
1
function ordinal($number){

    $last=substr($number,-1);
    if( $last>3 || $last==0 || ( $number >= 11 && $number <= 19 ) ){
      $ext='th';
    }else if( $last==3 ){
      $ext='rd';
    }else if( $last==2 ){
      $ext='nd';
    }else{
      $ext='st';
    }
    return $number.$ext;
  }
хуг
источник
0

Нашел ответ в PHP.net

<?php
function ordinal($num)
{
    // Special case "teenth"
    if ( ($num / 10) % 10 != 1 )
    {
        // Handle 1st, 2nd, 3rd
        switch( $num % 10 )
        {
            case 1: return $num . 'st';
            case 2: return $num . 'nd';
            case 3: return $num . 'rd';  
        }
    }
    // Everything else is "nth"
    return $num . 'th';
}
?>
mysticmo
источник
0

Вот еще одна очень короткая версия с использованием функций даты. Он работает для любого числа (без ограничений по дням месяца) и учитывает, что * 11 * 12 * 13 не соответствует формату * 1 * 2 * 3.

function getOrdinal($n)
{
    return $n . date_format(date_create('Jan ' . ($n % 100 < 20 ? $n % 20 : $n % 10)), 'S');
}
Клэр Мэтьюз
источник
-1

Я люблю этот маленький фрагмент

<?php

  function addOrdinalNumberSuffix($num) {
    if (!in_array(($num % 100),array(11,12,13))){
      switch ($num % 10) {
        // Handle 1st, 2nd, 3rd
        case 1:  return $num.'st';
        case 2:  return $num.'nd';
        case 3:  return $num.'rd';
      }
    }
    return $num.'th';
  }

?>

ВОТ

niksmac
источник
1
Я не совсем уверен, что этот ответ предлагает в дополнение к ответу ChintanThummar. Ну, это намекает на то, что ChintanThummar допустил нарушение авторских прав, если только он не написал код в вашем источнике ...
Андреас Рейбранд