После обсуждения с некоторыми моими коллегами у меня возник «философский» вопрос о том, как относиться к типу данных char в Java, следуя рекомендациям.
Предположим, что простой сценарий (очевидно, это всего лишь очень простой пример, чтобы придать практический смысл моему вопросу), где, учитывая в качестве входных данных строку String, необходимо посчитать количество числовых символов, присутствующих в нем.
Это 2 возможных решения:
1)
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) >= 48 && s.charAt(i) <= 57) {
n++;
}
}
2)
for(int i=0; i<s.length(); i++) {
if(s.charAt(i) >= '0' && s.charAt(i) <= '9' ) {
n++;
}
}
Какой из этих двух вариантов более «чистый» и соответствует лучшим практикам Java?
VK_
константы, которые вы должны использовать, во-вторых, использование кодов символов лучше, чем char. Java - это типобезопасный язык, который не должен выполнять перекрестную проверку типов. @Brandin Это называется практика кодированияVK_*
Константы соответствуют ключам, а не символам .Ответы:
Оба ужасны, но первый ужаснее.
Оба игнорируют встроенную в Java возможность определять, какие символы являются «числовыми» (с помощью методов in
Character
). Но первый не только игнорирует природу Unicode строк, предполагая, что может быть только 0123456789, но также затеняет даже это недопустимое рассуждение, используя коды символов, которые имеют смысл, только если вы что-то знаете об истории кодировок символов.источник
matches("[0-9]+")
, а не использовать исторически мотивированный трюк диапазона.Ни. Позвольте встроенному в Java классу Character понять это за вас.
Есть несколько больше диапазонов символов, чем цифры ASCII, которые считаются цифрами, и ни один из опубликованных вами примеров не будет их считать. JavaDoc для
Character.isDigit()
списков этих диапазонов символов , как быть действительные цифры:При этом следует делегировать
Character.isDigit()
даже с этим списком. По мере заполнения новых плоскостей Unicode код Java будет обновляться. Обновление JVM может обеспечить беспроблемную работу старого кода с новыми символами. Это также СУХОЙ : локализуя код «это цифра» в одном месте, на которое есть ссылки в другом месте, можно избежать негативных аспектов дублирования кода (например, ошибок). Наконец, обратите внимание на последнюю строку: этот список не является исчерпывающим, и есть другие цифры.Лично я предпочел бы делегировать основные библиотеки Java и тратить свое время на более производительные задачи, чем на «вычисление, что такое цифра».
Единственное исключение из этого правила - если вам действительно нужно проверять буквенные цифры ASCII, а не другие цифры. Например, если вы разбор поток и только ASCII цифр (в отличии от других цифр) имеет особое значение, то это было бы не целесообразно использовать
Character.isDigit()
.В этом случае я бы написал другой метод, например,
MyClass.isAsciiDigit()
и добавил бы логику. Вы получаете те же преимущества повторного использования кода, имя очень ясно, что он проверяет, и логика верна.источник
Если вы когда-нибудь напишите приложение на C, которое использует EBCDIC в качестве базового набора символов и нуждается в обработке символов ASCII, тогда используйте
48
и57
. Ты это делаешь? Я так не думаю.Об использовании
isDigit()
: это зависит. Вы пишете парсер JSON? Только0
к9
принимаются как цифры, так что не используйтеisDigit()
, проверьте>= '0'
и<= '9'
. Вы обрабатываете пользовательский ввод? ИспользуйтеisDigit()
до тех пор, пока остальная часть вашего кода действительно может обработать строку и превратить ее в число правильно.источник
Второй пример явно лучше. Смысл второго примера сразу становится очевидным, когда вы смотрите на код. Смысл первого примера очевиден, только если вы запомнили всю таблицу ASCII в своей голове.
Вы должны различать проверку определенного символа или проверку диапазона или класса символов.
1) Проверка на конкретного персонажа.
Для обычных символов используйте литеру, например,
if(ch=='z')...
. Если вы проверяете наличие специальных символов, таких как табуляция или разрыв строки, вам следует использовать экранирование, напримерif (ch=='\n')...
. Если проверяемый вами символ необычен (например, не распознается сразу или недоступен на стандартной клавиатуре), вы можете использовать шестнадцатеричный код символа, а не буквальный символ. Но поскольку шестнадцатеричный код является «магическим значением», вы должны извлечь его в константу и задокументировать его:Шестнадцатеричные коды - это стандартный способ задания кодов символов.
2) Проверка класса персонажа или диапазона
Вы действительно не должны делать это непосредственно в коде приложения, но должны заключать его в отдельный класс, относящийся только к классификации символов. И вам следует изменить это, поскольку библиотеки уже существуют для этой цели, а классификация символов обычно более сложна, чем вы думаете, по крайней мере, если вы рассматриваете символы вне ASCII-диапазона.
Если вас интересуют только символы в диапазоне ASCII, вы можете использовать символьные литералы в этой библиотеке, в противном случае вы, вероятно, будете использовать шестнадцатеричные литералы. Если вы посмотрите на исходный код встроенной библиотеки символов Java, он также ссылается на шестнадцатеричные значения и диапазоны символов, так как они указаны в стандарте Unicode.
источник
'\x2603'
формате, используя вместо этого явное указание, что вы проверяете значение для символа с шестнадцатеричной кодировкой, а не просто для любого случайного числа.Всегда лучше использовать,
c >= '0'
потому чтоc >= 48
вам нужно конвертировать c в коде ascii.источник
Регулярные выражения ( RegEx ) имеют специальный символьный класс для цифр,
\d
который можно использовать для удаления любого другого символа из вашей строки. Длина полученной строки является желаемым значением.Обратите внимание, однако, что RegEx s в вычислительном отношении более требовательны, чем другие предложенные решения, поэтому они не должны быть в целом предпочтительными .
источник