Чи краще перевірити `c> = '0'` або` c> = 48`?


46

Після дискусії з деякими моїми колегами у мене виникло "філософське" запитання про те, як поводитися з типом даних char на Java, дотримуючись кращих практик.

Припустимо, простий сценарій (очевидно, це лише дуже простий приклад для того, щоб надати моєму значенню питання), де, враховуючи рядок "Вхід", ви повинні підрахувати кількість числових символів, присутніх у ньому.

Це два можливі рішення:

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?


141
Чому б ви писали 48 і 57, коли ви насправді маєте на увазі "0" і "9"? Просто напишіть, що ви маєте на увазі.
Брандін

9
Зачекайте, що ви робите, у Java є VK_константи, які ви повинні використовувати, по-друге, використання char-кодів краще, ніж char Java - це безпечна мова, яку ви не повинні робити перевірки міжхресного типу. @Brandin Це називається практикою кодування
Мартін Баркер

12
Не турбуючись зробити більше, ніж судити про 6 людей, ЯКІ ДУМАЮТЬСЯ ЦЕ ДОБРЕ ПИТАННЯ. Чи використовуєте ви символи як числа? Якщо так, використовуйте цифри. Ви використовуєте це як букви? Якщо так, використовуйте букви.
Алек Тіл

17
@MartinBarker VK_*Константи відповідають клавішам, а не символам .
CodesInChaos

2
Мені знадобилось кілька хвилин, щоб визначити, що робить цей код стосовно вашого запитання. Вже це не зрозуміло, оскільки передбачається, що я знаю в (1), що я знаю, що це діапазон знаків ISO-латинська 1. Тож це робить проблематичним з точки зору технічного обслуговування.
CyberSkull

Відповіді:


124

Обидва жахливі, але перший - жахливіший.

Обидва ігнорують вбудовану можливість Java визначати, які символи є "числовими" (за допомогою методів в Character). Але перший не лише ігнорує природу рядків Unicode, припускаючи, що їх може бути лише 0123456789, але також затьмарює навіть це невірне міркування, використовуючи коди символів, які мають сенс лише, якщо ви знаєте щось про історію кодування символів.


33
Чому ви вважаєте, що відхилення цифр, що не належать до ASCII, є неправильним? Це залежить від контексту.
CodesInChaos

21
@CodesInChaos Якщо ви дійсно хочете знайти числові символи, сканування для 0123456789 очевидно неправильне. Якщо ви дійсно хочете сканувати лише ці десять символів, то вони по суті є безглуздими маркерами, які лише випадково виглядають знайомими для людей, які знають лише ASCII / ISO-латинську мову. У цьому немає нічого поганого - мені часто доводиться робити саме це, наприклад взаємодіяти зі застарілим програмним забезпеченням, яке дійсно приймає лише ті десять символів. Але тоді слід уточнити свої наміри, використовуючи щось на кшталт matches("[0-9]+"), а не використовувати історично мотивований фокус із діапазоном.
Кіліан Фот

15
Існують цифри повної ширини , схожі на цифри ASCII, і взагалі потрібно багато програмного забезпечення, щоб прийняти їх замість цифр ASCII. (Очевидно, багато програмного забезпечення зламано, залежно від визначення "багато". Ви можете це легко сказати, оскільки постачальникам програмного забезпечення в одній країні неможливо продати іншій країні, оскільки постачальники не відповідають вимогам інших країн. )
rwong

37
I have a Japanese IME installed , and accidentally type in full - all the time
BlueRaja - Danny Pflughoeft

14
"Обидва жахливі", але ви забули сказати правильне рішення ;-)
Кромстер каже, що підтримує Моніку

163

Ні. Дозвольте вбудованому класу символів Java зрозуміти це для вас.

for (int i = 0; i < s.length(); ++i) {
  if (Character.isDigit(s.charAt(i))) {
    ++n;
  }
}

Існує ще кілька діапазонів символів, ніж цифри ASCII, які вважаються цифрами, і жоден приклад, який ви опублікували, не вважатиме їх. JavaDoc для Character.isDigit()списків цих діапазонів символів , як бути дійсні цифри:

Деякі діапазони символів Unicode, які містять цифри:

  • '\ u0030' через '\ u0039', цифри ISO-LATIN-1 ('0' до '9')
  • '\ u0660' через '\ u0669', арабсько-індійські цифри
  • '\ u06F0' через '\ u06F9', розширені арабсько-індійські цифри
  • '\ u0966' через '\ u096F', цифри Devanagari
  • '\ uFF10' через '\ uFF19', цифри повної ширини

Багато інших діапазонів символів також містять цифри.

Зважаючи на це, варто делегувати Character.isDigit()навіть з цим списком. Коли нові площини Unicode заповнені, код Java буде оновлений. Оновлення JVM могло б змусити роботу старого коду з новими символами. Також DRY : по локалізації «це цифра» код в одне місце , яке згадується, негативні аспекти дублювання коди (тобто клопи) можна уникнути. Нарешті, зауважте останній рядок: цей список не є вичерпним, і є інші цифри.

Особисто я вважаю за краще делегувати основні бібліотеки Java і витрачати свій час на більш продуктивні завдання, ніж на "розбір того, що є цифрою".


Єдиним винятком із цього правила є те, якщо вам дійсно потрібно перевірити наявність буквальних цифр ASCII, а не інших цифр. Наприклад, якщо ви розбір потік і тільки ASCII цифр (на відміну від інших цифр) має особливе значення, то це було б НЕ доцільно використовувати Character.isDigit().

У такому випадку я б написав інший метод, наприклад, MyClass.isAsciiDigit()і вклав би туди логіку. Ви отримуєте ті ж переваги повторного використання коду, ім’я є надто зрозумілим щодо того, що він перевіряє, а логіка правильна.


4
Прекрасна відповідь за те, що насправді надає чистий код, який робить трюк.
П’єр Арло

27

Якщо ви коли-небудь пишете програму на C, яка використовує EBCDIC як основний набір символів та потребує обробки символів ASCII, тоді використовуйте 48та 57. Ти це робиш? Я не думаю, що так.

Про використання isDigit(): це залежить. Ви пишете парсер JSON? Тільки, 0щоб 9прийняти їх як цифри, тому не використовуйте isDigit(), не перевіряйте >= '0'та <= '9'. Чи обробляєте Ви дані користувача? Використовуйте до isDigit()тих пір, поки решта вашого коду насправді зможе обробити рядок і правильно перетворити його в число.


3
Насправді ви можете писати програми на Java, яка отримує та повертає EBCDIC. Це не весело.
Thorbjørn Ravn Andersen

Подібний "не весело" переглядав код, написаний з використанням десяткових знаків символів EBCDIC при перетворенні його на міжплатформене середовище ...
Гвін Еванс

1
Якщо ви обробляєте дані EBCDIC в Java, вам, ймовірно, слід перетворити їх у нативну схему Java UTF-16, перш ніж обробляти їх як символи. Але я здогадуюсь, що насправді залежить від програми; сподіваємось, якщо ваша програма має справу з EBCDIC, тоді ви зрозумієте, що потрібно зробити.
Майкл Берр

1
Основний момент полягає в тому, що для обробки EBCDIC на Java як "0", так і 48 неправильно виявити цифру нуля. Більш поточні в C, C ++ і т.д. '\ n' та '\ r' визначені реалізацією, тому, якщо ви хочете виявити пару файлів CR / LF Windows у файлі за допомогою компілятора, який не є windows, краще перевірте десяткові значення замість перевірка "\ n" та "\ r".
gnasher729

12

Другий приклад явно вищий. Значення другого прикладу відразу очевидно, коли ви дивитесь на код. Сенс першого прикладу очевидний лише в тому випадку, якщо ви запам'ятали всю таблицю ASCII в голові.

Ви повинні розрізняти перевірку на певний символ або перевірку на діапазон або клас символів.

1) Перевірка конкретного символу.

Для звичайних символів використовуйте літеру, наприклад, if(ch=='z').... Якщо ви перевіряєте, чи немає таких спеціальних символів, як перерва вкладки чи переривання рядків, вам слід скористатися втечами, наприклад if (ch=='\n').... Якщо символ, який ви перевіряєте, незвичний (наприклад, його не відразу впізнають або не доступні на стандартній клавіатурі), ви можете використовувати шістнадцятковий код символів, а не буквальний символ. Але оскільки шістнадцятковий код - це "магічне значення", ви отримаєте його до константи та документуєте:

const char snowman = 0x2603; // snowman char used to detect encoding issues
...
if (ch==showman)...

Шістнадцяткові коди - це стандартний спосіб визначення символьних кодів.

2) Перевірка класу чи діапазону символів

Вам дійсно не слід робити це безпосередньо в коді програми, а слід інкапсулювати його в окремий клас, що стосується лише класифікації символів. І вам слід відрізнятись від цього, оскільки бібліотеки для цієї мети вже існують, і класифікація символів зазвичай є більш складною, ніж ви думаєте, принаймні, якщо ви вважаєте символи поза діапазоном ASCII.

Якщо вас турбують лише символи в діапазоні ASCII, ви можете використовувати літерали символів у цій бібліотеці, інакше ви, ймовірно, будете використовувати шістнадцяткові літерали. Якщо ви подивитеся на вихідний код бібліотеки символів, побудований на Java, він також посилається на значення символів та діапазони, використовуючи шістнадцятковий, оскільки саме так вони вказані у стандарті Unicode.


1
Я також рекомендую писати буквений символ у шістнадцятковій формі, використовуючи '\x2603'натомість чітко, що ви тестуєте значення для символу з шістнадцятковим кодуванням, а не просто будь-яким випадковим числом.
wefwefa3

-4

Це завжди краще використовувати, c >= '0'тому що для c >= 48вас потрібно перетворити c в код ascii.


3
Що означає ця відповідь, про що вже не було сказано в попередніх відповідях від тижня тому?

-5

Регулярні вирази ( RegEx ) мають специфічний клас символів для цифр - \d-, який може бути використаний для видалення будь-якого іншого символу з рядка. Довжина отриманого рядка - бажане значення.

public static int countDigits(String str) {
    str = Objects.requireNonNull(str).trim();

    return str.replaceAll("[^\\d]", "").length();
}

Зауважте, однак, що RegEx s обчислювально більш вимогливі, ніж інші запропоновані рішення, тому їх загалом не слід віддавати перевагу .


Дуже елегантний спосіб зробити перевірку!
Кевін Робатель

Regexes є надмірним для подібного завдання
Pharap

2
@StefanoBragaglia Після перечитання вашої відповіді, я думаю, це не відповідає на питання.
Фарап

2
Ваша відповідь передбачає інший спосіб вирішення проблеми "як я рахую цифри в рядку". Він не відповідає основній проблемі із зразками коду та поданням констант - чи цифрами, чи символами.

2
Це насправді не підраховує цифри (це просто говорить вам, яка довжина рядка після того, як ви видалили всі цифри, яких немає ні тут, ні там), але я згоден, що це фактично не відповідає на питання. Як, наприклад, ніхто не питав про видалення символів з рядків. Питання просто запитує про відповідний спосіб найкращої практики перевірити, чи є персонаж числовим.
doppelgreener
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.