Статьи

ChoiceFormat: форматирование числового диапазона

В Javadoc для класса ChoiceFormat говорится, что ChoiceFormat «позволяет вам присоединять формат к диапазону чисел» и «обычно используется в MessageFormat для обработки множественного числа». Этот пост описывает java.text.ChoiceFormat и предоставляет несколько примеров его применения в коде Java.

Одно из наиболее заметных отличий между ChoiceFormat и другими классами « format » в пакете java.text заключается в том, что ChoiceFormat не предоставляет статические методы для доступа к экземплярам ChoiceFormat . Вместо этого ChoiceFormat предоставляет два конструктора, которые используются для создания экземпляров объектов ChoiceFormat . Javadoc для ChoiceFormat подчеркивает и объясняет это:


ChoiceFormat отличается от других классов Format тем, что вы создаете объект ChoiceFormat с помощью конструктора (а не метода фабрики стилей getInstance). Методы фабрики не нужны, потому что ChoiceFormat не требует какой-либо сложной настройки для данной локали. Фактически, ChoiceFormat не реализует никакого поведения, специфичного для локали.

Построение формата выбора с двумя массивами

Первый из двух конструкторов, предоставляемых ChoiceFormat принимает в качестве аргументов два массива. Первый массив — это массив примитивных двойников, которые представляют наименьшее значение (начальное значение) каждого интервала. Второй массив — это массив строк, которые представляют имена, связанные с каждым интервалом. Два массива должны иметь одинаковое количество элементов, поскольку между числовыми (двойными) интервалами и строками, описывающими эти интервалы, предполагается однозначное сопоставление. Если два массива не имеют одинаковое количество элементов, возникает следующее исключение.


Исключение в потоке «main» java.lang.IllegalArgumentException: Массивы и лимиты должны иметь одинаковую длину.

Javadoc для конструктора ChoiceFormat (double [], String []) утверждает, что первый параметр массива называется «limit», имеет тип double[] и описывается как «limit в возрастающем порядке». Второй параметр массива называется «format», имеет тип String[] и описывается как «соответствующие строки формата». Согласно Javadoc, этот конструктор «конструирует с ограничениями и соответствующими форматами».

Использование конструктора ChoiceFormat принимающего два аргумента массива, продемонстрировано в следующем листинге кода (метод writeGradeInformation(ChoiceFormat) и переменная fredsTestScores будут показаны позже).

01
02
03
04
05
06
07
08
09
10
11
12
/**
 * Demonstrate ChoiceFormat instantiated with ChoiceFormat
 * constructor that accepts an array of double and an array
 * of String.
 */
public void demonstrateChoiceFormatWithDoublesAndStringsArrays()
{
   final double[] minimumPercentages = {0, 60, 70, 80, 90};
   final String[] letterGrades = {"F", "D", "C", "B", "A"};
   final ChoiceFormat gradesFormat = new ChoiceFormat(minimumPercentages, letterGrades);
   writeGradeInformation(fredsTestScores, gradesFormat);
}

Приведенный выше пример соответствует ожиданиям проиллюстрированного конструктора ChoiceFormat . Два массива имеют одинаковое количество элементов, первый ( double[] ) массив имеет свои элементы в порядке возрастания, а второй ( String[] ) массив имеет свои «форматы» в том же порядке, что и соответствующие пределы начала интервала в первом массиве.

Метод writeGradeInformation(ChoiceFormat) упомянутый в приведенном выше фрагменте кода, демонстрирует использование экземпляра ChoiceFormat на основе двух массивов для «форматирования» предоставленных числовых значений в виде строк. Реализация метода показана далее.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Write grade information to standard output
 * using the provided ChoiceFormat instance.
 *
 * @param testScores Test Scores to be displayed with formatting.
 * @param gradesFormat ChoiceFormat instance to be used to format output.
 */
public void writeGradeInformation(
   final Collection<Double> testScores,
   final ChoiceFormat gradesFormat)
{
   double sum = 0;
   for (final Double testScore : testScores)
   {
      sum += testScore;
      out.println(testScore + " is a '" + gradesFormat.format(testScore) + "'.");
   }
   double average = sum / testScores.size();
   out.println(
        "The average score (" + average + ") is a '"
      + gradesFormat.format(average) + "'.");
}

Приведенный выше код использует экземпляр ChoiceFormat предоставленный для «форматирования» результатов тестов. Вместо печати числового значения «format» печатает строку, связанную с интервалом, в который попадает числовое значение. В следующем листинге кода показано определение fredsTestScores использованное в этих примерах.

01
02
03
04
05
06
07
08
09
10
private static List<Double> fredsTestScores;
static
{
   final ArrayList<Double> scores = new ArrayList<>();
   scores.add(75.6);
   scores.add(88.8);
   scores.add(97.3);
   scores.add(43.3);
   fredsTestScores = Collections.unmodifiableList(scores);
}

Выполнение этих тестовых оценок через экземпляр ChoiceFormat с двумя массивами, приводит к следующему выводу:

1
2
3
4
5
75.6 is a 'C'.
88.8 is a 'B'.
97.3 is a 'A'.
43.3 is a 'F'.
The average score (76.25) is a 'C'.

Построение ChoiceFormat со строкой шаблона

Конструктор ChoiceFormat(String) который принимает шаблон на основе строк, может быть более привлекательным для разработчиков, которым удобно использовать шаблон на основе строк с аналогичными классами форматирования, такими как DateFormat и DecimalFormat . Следующий листинг кода демонстрирует использование этого конструктора. Шаблон, предоставленный конструктору, приводит к экземпляру ChoiceFormat который должен форматироваться так же, как экземпляр ChoiceFormat созданный в предыдущем примере с конструктором, который принимает два массива.

01
02
03
04
05
06
07
08
09
10
/**
 * Demonstrate ChoiceFormat instantiated with ChoiceFormat
 * constructor that accepts a String pattern.
 */
public void demonstrateChoiceFormatWithStringPattern()
{
   final String limitFormatPattern = "0#F | 60#D | 70#C | 80#B | 90#A";
   final ChoiceFormat gradesFormat = new ChoiceFormat(limitFormatPattern);
   writeGradeInformation(fredsTestScores, gradesFormat);
}

Метод writeGradeInformation вызываемый здесь, такой же, как и метод, вызываемый ранее, и результат также тот же (здесь не показан, потому что он тот же).

ChoiceFormat Поведение на крайностях и границах

До сих пор примеры хорошо работали с результатами тестов в ожидаемых диапазонах. Еще один набор результатов тестов теперь будет использоваться для демонстрации некоторых других функций ChoiceFormat . Этот новый набор тестовых оценок настроен в следующем листинге кода и включает в себя «невозможный» отрицательный балл и другой «вероятный невозможный» балл выше 100.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private static List<Double> boundaryTestScores;
static
{
   final ArrayList<Double> boundaryScores = new ArrayList<Double>();
   boundaryScores.add(-25.0);
   boundaryScores.add(0.0);
   boundaryScores.add(20.0);
   boundaryScores.add(60.0);
   boundaryScores.add(70.0);
   boundaryScores.add(80.0);
   boundaryScores.add(90.0);
   boundaryScores.add(100.0);
   boundaryScores.add(115.0);
   boundaryTestScores = boundaryScores;
}

Когда вышеприведенный набор тестовых оценок проходит через один из созданных ранее экземпляров ChoiceFormat , вывод будет таким, как показано ниже.

01
02
03
04
05
06
07
08
09
10
-25.0 is a 'F '.
0.0 is a 'F '.
20.0 is a 'F '.
60.0 is a 'D '.
70.0 is a 'C '.
80.0 is a 'B '.
90.0 is a 'A'.
100.0 is a 'A'.
115.0 is a 'A'.
The average score (56.666666666666664) is a 'F '.

Только что показанный вывод демонстрирует, что «пределы», установленные в конструкторах ChoiceFormat являются «включающими», что означает, что эти ограничения применяются к указанному пределу и выше (до следующего предела). Другими словами, диапазон числа определяется как больше или равно указанному пределу. Документация Javadoc для ChoiceFormat описывает это с помощью математического описания:


X соответствует j тогда и только тогда, когда limit [j] ≤ X <limit [j + 1]

Выходные данные из примера результатов тестов границ также демонстрируют другую характеристику ChoiceFormat описанную в его документации Javadoc: «Если совпадений нет, то используется либо первый, либо последний индекс, в зависимости от того, слишком мало или слишком мало число (X) высокий.» Поскольку в предоставленных экземплярах ChoiceFormat нет совпадения для -25.0, самый низкий («F» для предела 0) диапазон применяется к этому числу, меньшему, чем самый низкий диапазон. В этих примерах результатов теста не установлено более высокого предела, чем «90» для «A», поэтому все оценки выше 90 (включая оценки выше 100) относятся к «A». Предположим, что мы хотим, чтобы диапазоны оценок были от 0 до 100, или чтобы отформатированный результат указывал «Неверно» для оценок менее 0 или более 100. Это можно сделать, как показано в следующем листинге кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
/**
 * Demonstrating enforcing of lower and upper boundaries
 * with ChoiceFormat instances.
 */
public void demonstrateChoiceFormatBoundariesEnforced()
{
   // Demonstrating boundary enforcement with ChoiceFormat(double[], String[])
   final double[] minimumPercentages = {Double.NEGATIVE_INFINITY, 0, 60, 70, 80, 90, 100.000001};
   final String[] letterGrades = {"Invalid - Too Low", "F", "D", "C", "B", "A", "Invalid - Too High"};
   final ChoiceFormat gradesFormat = new ChoiceFormat(minimumPercentages, letterGrades);
   writeGradeInformation(boundaryTestScores, gradesFormat);
 
   // Demonstrating boundary enforcement with ChoiceFormat(String)
   final String limitFormatPattern = "-\u221E#Invalid - Too Low | 0#F | 60#D | 70#C | 80#B | 90#A | 100.0<Invalid - Too High";
   final ChoiceFormat gradesFormat2 = new ChoiceFormat(limitFormatPattern);
   writeGradeInformation(boundaryTestScores, gradesFormat2);
}

Когда вышеуказанный метод выполняется, его вывод показывает, что оба подхода обеспечивают соблюдение граничных условий лучше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
-25.0 is a 'Invalid - Too Low'.
0.0 is a 'F'.
20.0 is a 'F'.
60.0 is a 'D'.
70.0 is a 'C'.
80.0 is a 'B'.
90.0 is a 'A'.
100.0 is a 'A'.
115.0 is a 'Invalid - Too High'.
The average score (56.666666666666664) is a 'F'.
-25.0 is a 'Invalid - Too Low '.
0.0 is a 'F '.
20.0 is a 'F '.
60.0 is a 'D '.
70.0 is a 'C '.
80.0 is a 'B '.
90.0 is a 'A '.
100.0 is a 'A '.
115.0 is a 'Invalid - Too High'.
The average score (56.666666666666664) is a 'F '.

В последнем листинге кода демонстрируется использование Double.NEGATIVE_INFINITY и \u221E ( символ Unicode INFINITY ) для установления границы минимально возможного предела в каждом из примеров. Чтобы оценки выше 100.0 были отформатированы как недопустимые, в ChoiceFormat основе ChoiceFormat в качестве нижнего предела этого недопустимого диапазона используется число, немного превышающее 100. Экземпляр ChoiceFormat основе строковых / шаблонных ChoiceFormat обеспечивает большую гибкость и точность при указании нижнего предела диапазона «Недопустимый — слишком высокий» для любого числа больше 100,0 с использованием символа «меньше» (<).

Обработка None, Singular и Plural с помощью ChoiceFormat

Я открыл этот пост, процитировав Javadoc, заявив, что ChoiceFormat «обычно используется в MessageFormat для обработки множественного числа», но еще не продемонстрировал это общее использование в этом посте. Я продемонстрирую часть этого (множественное число без MessageFormat ) очень кратко здесь для полноты, но гораздо более полное объяснение (множественное число с MessageFormat ) этого распространенного использования ChoiceFormat доступно в уроке Обработка множественных ChoiceFormat в Учебниках Java (часть Интернационализации). след ).

Следующий листинг кода демонстрирует применение ChoiceFormat для обработки единственного и множественного числа.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
 * Demonstrate ChoiceFormat used for differentiation of
 * singular from plural and none.
 */
public void demonstratePluralAndSingular()
{
   final double[] cactiLowerLimits = {0, 1, 2, 3, 4, 10};
   final String[] cactiRangeDescriptions =
      {"no cacti", "a cactus", "a couple cacti", "a few cacti", "many cacti", "a plethora of cacti"};
   final ChoiceFormat cactiFormat = new ChoiceFormat(cactiLowerLimits, cactiRangeDescriptions);
   for (int cactiCount = 0; cactiCount < 11; cactiCount++)
   {
      out.println(cactiCount + ": I own " + cactiFormat.format(cactiCount) + ".");
   }
}

Выполнение примера в последнем листинге кода приводит к выводу, который показан далее.

01
02
03
04
05
06
07
08
09
10
11
0: I own no cacti.
1: I own a cactus.
2: I own a couple cacti.
3: I own a few cacti.
4: I own many cacti.
5: I own many cacti.
6: I own many cacti.
7: I own many cacti.
8: I own many cacti.
9: I own many cacti.
10: I own a plethora of cacti.

Один последний символ, поддерживаемый шаблоном ChoiceFormat

Другим символом, который ChoiceFormat синтаксический анализ шаблона ChoiceFormat для форматирования строк из сгенерированного числового значения, является \u2264 ( ). Это продемонстрировано в следующем листинге кода и выводе этого кода, который следует за листингом кода. Обратите внимание, что в этом примере \u2264 работает так же, как и при использовании более простого знака # показанного ранее.

01
02
03
04
05
06
07
08
09
10
11
/**
 * Demonstrate using \u2264 in String pattern for ChoiceFormat
 * to represent >= sign. Treated differently than less-than
 * sign but similarly to #.
 */
public void demonstrateLessThanOrEquals()
{
   final String limitFormatPattern = "0\u2264F | 60\u2264D | 70\u2264C | 80\u2264B | 90\u2264A";
   final ChoiceFormat gradesFormat = new ChoiceFormat(limitFormatPattern);
   writeGradeInformation(fredsTestScores, gradesFormat);
}
1
2
3
4
5
75.6 is a 'C '.
88.8 is a 'B '.
97.3 is a 'A'.
43.3 is a 'F '.
The average score (76.25) is a 'C '.

Наблюдения в обзоре

В этом разделе я кратко ChoiceFormat некоторые замечания, касающиеся ChoiceFormat сделанные в ходе этого поста, и его примеры.

  • При использовании конструктора ChoiceFormat (double [], String []) два переданных массива должны иметь одинаковый размер, иначе будет выброшено исключение IllegalArgumentException («Массив и лимитный массив должны иметь одинаковую длину.»).
  • Массив « limit » double[] предоставляемый конструктору ChoiceFormat (double [], String []), должен иметь пределы, перечисленные слева направо в порядке возрастания чисел. Если это не так, исключение не выдается, но логика почти наверняка не будет правильной, поскольку строки, отформатированные для экземпляра ChoiceFormat будут «совпадать» неправильно. То же самое ожидание относится к конструктору, принимающему шаблон.
  • ChoiceFormat позволяет использовать Double.POSITIVE_INFINITY и Double.NEGATIVE_INFINITY для указания нижних пределов диапазона с помощью конструктора из двух массивов.
  • ChoiceFormat позволяет использовать \u221E и -\u221E для указания нижних пределов диапазона с помощью своего единственного конструктора String (pattern).
  • Конструктор ChoiceFormat принимающий шаблон String, немного более гибок, чем конструктор с двумя массивами, и позволяет задавать границы нижнего предела как все в пределах определенного количества, не включая это определенное количество точно.
  • Символы и символы со специальным значением в шаблонах String, предоставляемых одному конструктору String ChoiceFormat включают # , < , \u2264 ( ), \u221E ( ) и | ,

Вывод

ChoiceFormat позволяет настраивать числовые диапазоны так, чтобы определенные диапазоны могли иметь разные и конкретные представления. Этот пост охватывал несколько различных аспектов форматирования числовых диапазонов с помощью ChoiceFormat , но анализ числовых диапазонов из строк с использованием ChoiceFormat в этом посте не рассматривался.

Дальнейшее чтение