Po dyskusji z kilkoma moimi kolegami mam „filozoficzne” pytanie o to, jak traktować typ danych char w Javie, zgodnie z najlepszymi praktykami.
Załóżmy, że jest to prosty scenariusz (oczywiście jest to tylko bardzo prosty przykład, aby nadać praktyce znaczenie moje pytanie), w którym biorąc pod uwagę ciąg znaków jako dane wejściowe, musisz policzyć liczbę obecnych w nim znaków numerycznych.
Oto 2 możliwe rozwiązania:
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++;
}
}
Który z nich jest bardziej „czysty” i zgodny z najlepszymi praktykami Java?
VK_
stałe, których powinieneś używać, po drugie używanie kodów char jest lepsze niż char Java to bezpieczny język typu, którego nie powinieneś sprawdzać między typami. @Brandin Nazywa się to praktykami kodowaniaVK_*
Stałe odpowiadają kluczom, a nie znakom .Odpowiedzi:
Oba są okropne, ale pierwszy jest bardziej okropny.
Oba ignorują wbudowaną zdolność Javy do decydowania, które znaki są „numeryczne” (poprzez metody w
Character
). Ale pierwszy nie tylko ignoruje unikodową naturę ciągów, zakładając, że może istnieć tylko 0123456789, ale również zaciemnia to niepoprawne rozumowanie za pomocą kodów znaków, które mają sens tylko wtedy, gdy wiesz coś o historii kodowania znaków.źródło
matches("[0-9]+")
, zamiast wykorzystywać historycznie motywowaną sztuczkę z zasięgiem.Ani. Pozwól, aby wbudowana klasa postaci w Javie zrozumiała to dla Ciebie.
Istnieje kilka zakresów znaków więcej niż cyfry ASCII, które liczą się jako cyfry, i żaden opublikowany przykład nie będzie ich liczyć. JavaDoc dla
Character.isDigit()
list tych zakresów znaków jako ważne cyfry:Biorąc to pod uwagę, należy delegować
Character.isDigit()
nawet na tę listę. W miarę zapełniania się nowych samolotów Unicode kod Java będzie aktualizowany. Aktualizacja JVM może sprawić, że stary kod będzie działał płynnie z nowymi cyframi. Jest to również OSUSZANIE : lokalizując kod „czy to cyfra” w jednym miejscu, do którego odwołuje się gdzie indziej, można uniknąć negatywnych aspektów powielania kodu (tj. Błędów). Na koniec zwróć uwagę na ostatnią linię: ta lista nie jest wyczerpująca i są inne cyfry.Osobiście wolałbym oddelegować do podstawowych bibliotek Java i spędzać czas na bardziej produktywnych zadaniach niż „zastanawiać się, co to jest cyfra”.
Jedynym wyjątkiem od tej reguły jest to, że naprawdę potrzebujesz przetestować dosłowne cyfry ASCII, a nie inne cyfry. Na przykład, jeśli analizujesz strumień i tylko cyfry ASCII (w przeciwieństwie do innych cyfr) mają specjalne znaczenie, to nie byłoby właściwe użycie
Character.isDigit()
.W takim przypadku napisałbym inną metodę, np.
MyClass.isAsciiDigit()
I umieściłbym tam logikę. Otrzymujesz te same korzyści z ponownego użycia kodu, nazwa jest bardzo jasna, co sprawdza, a logika jest poprawna.źródło
Jeśli kiedykolwiek napiszesz aplikację w C, która używa EBCDIC jako podstawowego zestawu znaków i musi przetwarzać znaki ASCII, użyj
48
i57
. Robisz to Nie wydaje mi sięO użyciu
isDigit()
: to zależy. Czy piszesz parser JSON? Tylko0
jako9
są akceptowane jako cyfry, więc nie używajisDigit()
, sprawdzaj>= '0'
i<= '9'
. Czy przetwarzasz dane wprowadzone przez użytkownika? UżyjisDigit()
tak długo, jak reszta kodu będzie w stanie obsłużyć ciąg i przekształcić go poprawnie w liczbę.źródło
Drugi przykład jest wyraźnie lepszy. Znaczenie drugiego przykładu jest natychmiast oczywiste, gdy spojrzysz na kod. Znaczenie pierwszego przykładu jest oczywiste tylko wtedy, gdy zapamiętałeś całą tabelę ASCII w swojej głowie.
Należy rozróżnić między sprawdzaniem określonego znaku, a sprawdzaniem zakresu lub klasy znaków.
1) Sprawdzanie określonej postaci.
W przypadku zwykłych znaków użyj literału znaku, np
if(ch=='z')...
. Jeśli porównujesz ze znakami specjalnymi, takimi jak tabulacja lub podział wiersza, powinieneś użyć znaków zmiany znaczenia, takich jakif (ch=='\n')...
. Jeśli znak, którego szukasz, jest nietypowy (np. Nie można go natychmiast rozpoznać lub nie jest dostępny na standardowej klawiaturze), możesz użyć kodu szesnastkowego zamiast literalnego. Ale ponieważ kod szesnastkowy jest „magiczną wartością”, wyodrębnisz go do stałej i udokumentujesz:Kody szesnastkowe to standardowy sposób określania kodów znaków.
2) Sprawdzanie klasy lub zasięgu postaci
Naprawdę nie powinieneś robić tego bezpośrednio w kodzie aplikacji, ale umieść go w osobnej klasie zajmującej się tylko klasyfikacją znaków. I powinieneś się różnić, ponieważ biblioteki już istnieją w tym celu, a klasyfikacja znaków jest zwykle bardziej złożona niż myślisz, przynajmniej jeśli weźmiesz pod uwagę znaki spoza zakresu ASCII.
Jeśli martwisz się tylko znakami z zakresu ASCII, możesz użyć literałów znaków w tej bibliotece, w przeciwnym razie prawdopodobnie użyłbyś literałów szesnastkowych. Jeśli spojrzysz na kod źródłowy wbudowanej biblioteki znaków Java, odnosi się on również do wartości i zakresów znaków za pomocą szesnastkowej, ponieważ w ten sposób są one określone w standardzie Unicode.
źródło
'\x2603'
zamiast tego, aby być jawnym, że testujesz wartość znaku z kodowaniem szesnastkowym, a nie tylko dowolną liczbą losową.Zawsze lepiej jest używać,
c >= '0'
ponieważc >= 48
musisz przekonwertować c w kodzie ascii.źródło
Wyrażenia regularne ( RegEx ) mają określoną klasę znaków dla cyfr -
\d
- której można użyć do usunięcia dowolnego innego znaku z łańcucha. Długość wynikowego ciągu jest pożądaną wartością.Zauważ jednak, że RegEx są obliczeniowo bardziej wymagające niż inne proponowane rozwiązania, dlatego nie powinny być ogólnie preferowane .
źródło