`C> = '0'` veya `c> = 48` değerini kontrol etmek daha iyi olur mu?


46

Bazı meslektaşlarım ile yaptığım görüşmeden sonra, en iyi uygulamaları izleyerek Java'daki karakter veri türünü nasıl ele aldığına dair 'felsefi' bir sorum var.

Basit bir senaryo düşünün (açıkçası bu, soruma anlam vermek için basit bir örnek.) , Bir Dize 's' girişi olarak verildiğinde, içinde bulunan sayısal karakterlerin sayısını saymanız gerekir.

Bunlar 2 olası çözümdür:

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++;
        }
    }

İkisinden hangisi daha 'temiz' ve Java en iyi uygulamalarıyla uyumlu?


141
Gerçekten '0' ve '9' demek istediğinde neden 48 ve 57 yazıyorsun? Sadece ne demek istediğini yaz.
Brandin

9
Ne yaptığınızı bekleyin, Java VK_kullanmanız gereken sabitlere sahiptir , ikincisi char kodlarını kullanmak char Java'dan daha iyidir; @Brandin Buna kodlama uygulamaları denir
Martin Barker

12
Daha fazlasını yapmak için uğraşmadan, BU İYİ BİR SORU olan İNANILAN 6 kişiyi yargılamak. Karakterleri sayı olarak mı kullanıyorsunuz? Eğer öyleyse sayıları kullanın. Harf olarak mı kullanıyorsun? Eğer öyleyse harfleri kullanın.
Alec Teal

17
@MartinBarker VK_*Sabitleri karakter olmayan tuşlara karşılık gelir .
KodlarInChaos

2
Bu kodun sorunuzla ilgili olarak ne yaptığını belirlemem birkaç dakika sürdü. Zaten net değil çünkü (1) 'de bunun ISO-Latin 1'in rakam aralığı olduğunu bildiğimi varsayıyor. Bu da bakım açısından problemli hale getiriyor.
CyberSkull

Yanıtlar:


124

Her ikisi de korkunç, ama birincisi daha korkunç.

Her ikisi de, hangi karakterlerin "sayısal" olduğuna karar vermek için Java'nın yerleşik özelliğini yok sayar (yöntemlerle Character). Fakat ilki, yalnızca 0123456789 olabileceğini varsayarsak, yalnızca Unicode dizelerinin yapısını görmezden gelmekle kalmaz, aynı zamanda , yalnızca karakter kodlamalarının tarihi hakkında bir şey biliyorsanız mantıklı olan karakter kodlarını kullanarak bu geçersiz mantığı bile gizler.


33
Neden ASCII dışı rakamları reddetmediğinin yanlış olduğunu düşünüyorsunuz? Bu bağlama bağlıdır.
KodlarInChaos

21
@CodesInChaos Sayısal karakterleri gerçekten bulmak istiyorsanız , 0123456789 için tarama yapmak yanlıştır. Yalnızca bu on karakteri taramak istiyorsanız, aslında yalnızca ASCII / ISO-Latince tanıyan insanlara yanlışlıkla tanıdık gelen anlamsız belirteçlerdir. Bunda yanlış olan bir şey yok - Genellikle tam olarak bunu yapmak zorundayım, örneğin, yalnızca bu on karakteri gerçekten kabul eden eski yazılımlarla etkileşime geçmek için. Ancak matches("[0-9]+"), tarihsel olarak motive edilmiş menzil numarasından istifade etmek yerine, benzer bir şeyi kullanarak niyetlerinizi netleştirmelisiniz .
Kilian Foth

15
ASCII rakamlarıyla aynı gibi görünen tam genişlikte rakamlar vardır ve genel olarak ASCII rakamlarının yerine kabul etmek için çok fazla yazılım gerekir. (Açıkçası "çok" un tanımına bağlı olarak çok fazla yazılım arızalı. Kolayca söyleyebilirsiniz çünkü bir ülkedeki yazılım satıcıları başka bir ülkeye satış yapmayı imkansız buluyor çünkü satıcılar diğer ülkelerin şartlarını yerine getirmiyor. )
Kasım’da

37
I have a Japanese IME installed , and accidentally type in full - width all the time..
BlueRaja - Danny Pflughoeft 25:15 te

14
"Her ikisi de korkunç", ancak doğru çözümü söylemeyi unuttun ;-)
Kromster, 5

163

Ne. Java'nın yerleşik Character sınıfının sizin için çözmesine izin verin.

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

Rakam olarak sayılan ASCII rakamlarından daha az sayıda karakter aralığı vardır ve gönderdiğiniz hiçbir örnek bunları saymaz. Javadoc için Character.isDigit()listeleri geçerli sayılar olarak bu karakter aralıkları:

Rakam içeren bazı Unicode karakter aralıkları:

  • '\ u0030' ila '\ u0039', ISO-LATIN-1 basamak ('0' ila '9')
  • '\ u0660' ile '\ u0669' arasında, Arapça-Hintçe basamaklar
  • '\ u06F0' ile '\ u06F9' arasında, Geniş Arapça-Hintçe rakamlar
  • '\ u0966' ile '\ u096F' arasında, Devanagari rakamları
  • '\ uFF10' ile '\ uFF19' arası, Tam genişlikli rakamlar

Diğer birçok karakter aralığı da basamak içerir.

Olduğu söyleniyor, Character.isDigit()bu liste ile bile bir temsilciye verilmesi gerekir . Yeni Unicode uçakları dolduruldukça, Java kodu güncellenecektir. JVM'yi yükseltmek eski kodun sorunsuz bir şekilde yeni rakam karakterleriyle çalışmasını sağlayabilir. Aynı zamanda KURU : başka bir yerde referans verilen bir yere "bu bir rakamdır" kodunu yerelleştirerek, kod çoğaltmanın (yani hataların) olumsuz yönlerinden kaçınılabilir. Son olarak, son satırı not edin: bu liste ayrıntılı değildir ve başka rakamlar vardır.

Şahsen, çekirdek Java kitaplıklarına vekâlet etmek ve zamanımı "rakamın ne olduğunu bulmaktan ziyade daha verimli işler için harcamayı tercih ederim."


Bu kuralın tek istisnası, gerçekten ASCII rakamları için test etmenize gerek olup olmadığını, diğer rakamları değil . Örneğin, bir akışı ayrıştırıyorsanız ve yalnızca ASCII rakamlarının (diğer rakamların aksine) özel bir anlamı varsa, kullanması uygun olmazCharacter.isDigit() .

Bu durumda, başka bir yöntem yazarım, mesela MyClass.isAsciiDigit()oraya mantığı koyardım. Kodun yeniden kullanımıyla aynı faydaları elde edersiniz, ad neyi kontrol ettiği konusunda net ve mantık doğrudur.


4
Aslında hile yapan temiz kodu sağlamak için mükemmel bir cevap.
Pierre Arlaud

27

Hiç süreç ASCII karakter temel karakter seti ve ihtiyaçlar olarak EBCDIC kullanan C bir uygulama yazarsanız sonra kullanmak 48ve 57. Bunu mu yapıyorsun? Sanmıyorum

Kullanma hakkında isDigit(): bağlıdır. Bir JSON ayrıştırıcısı mı yazıyorsunuz? Sadece 0için 9o kadar kullanmayın, basamak olarak kabul edilir isDigit()olup olmadığını kontrol edin >= '0've <= '9'. Kullanıcı girişi işliyor musunuz? Kullanım isDigit()sürece kodun kalanı aslında dize işleyebilir ve doğru bir dizi çevirmek olarak.


3
Aslında Java'da EBCDIC'i alan ve veren uygulamaları yazabilirsiniz. Bu eğlenceli değil.
Thorbjørn Ravn Andersen

Benzer 'eğlenceli değil' EBCDIC karakterlerinin ondalık platformlu bir ortama dönüştürülürken ondalık değerleri kullanılarak yazılan koddan geçiyordu ...
Gwyn Evans

1
Java'da EBCDIC verilerini işliyorsanız, karakter olarak işlemeden önce büyük olasılıkla onu Java yerel UTF-16 karakter kümesine dönüştürmeniz gerekir. Ama sanırım bu gerçekten uygulamaya bağlı; umarım eğer programınız EBCDIC ile uğraşırsa, ne yapılması gerektiğini anlarsınız.
Michael Burr

1
Ana nokta, Java’daki EBCDIC’i işlemek için hem '0' hem de 48’in sıfır rakamı tespit etmekte yanlış olduğu. Daha güncel, C, C ++ vb. '\ N' ve '\ r', uygulamada tanımlanmıştır, bu nedenle Windows CR / LF çiftini Windows olmayan bir derleyici kullanarak bir dosyada algılamak istiyorsanız, bunun yerine ondalık değerleri daha iyi kontrol edin. '\ n' ve '\ r' denetleniyor.
gnasher729

12

İkinci örnek açıkça üstündür. İkinci örneğin anlamı, koda baktığınızda hemen açıktır. İlk örneğin anlamı, kafanızdaki tüm ASCII masasını ezberlediyseniz açıktır.

Belirli bir karakterin kontrol edilmesi veya bir aralık veya karakter sınıfının kontrol edilmesi arasında ayrım yapmalısınız.

1) Belirli bir karakterin kontrolü.

Normal karakterler için, tam anlamıyla bir karakter kullanın, örn if(ch=='z').... Sekme veya satır sonu gibi özel karakterleri denetlerseniz, çıkış karakterlerini kullanmanız gerekir if (ch=='\n').... Denetlediğiniz karakter olağandışıysa (örneğin, hemen tanınmaz veya standart bir klavyede bulunmazsa), değişmez karakter yerine altıgen karakter kodunu kullanabilirsiniz. Ancak onaltılı kod bir "sihirli değer" olduğundan, onu sabit bir değere ayıklayıp belgelendirirsiniz:

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

Onaltılı kodlar, karakter kodlarını belirtmenin standart yoludur.

2) Bir karakter sınıfını veya aralığını kontrol etme

Bunu doğrudan uygulama kodunda yapmamalısınız, ancak sadece karakter sınıflandırmasıyla ilgili ayrı bir sınıfa yerleştirmelisiniz. Ve bu amaçla çeşitlilik göstermelisiniz, çünkü kütüphaneler bu amaç için zaten var ve karakter sınıflandırması, en azından ASCII aralığının dışındaki karakterleri düşünürseniz, düşündüğünüzden daha karmaşık.

Yalnızca ASCII aralığındaki karakterler hakkında endişe duyuyorsanız, bu kütüphanedeki karakter değişmezlerini kullanabilirsiniz, aksi takdirde muhtemelen hex değişmezleri kullanırsınız. Java yerleşik karakter kitaplığının kaynak koduna bakarsanız, Unicode standardında bu şekilde belirtildiklerinden onaltılık kullanarak karakter değerleri ve aralıkları da ifade edilir.


1
Ayrıca, '\x2603'sadece herhangi bir rasgele sayı değil, onaltılık kodlamaya sahip bir karakterin değerini test ettiğinizde açıkça kullandığınızdan emin olmak için , karakterin karakterini hex olarak yazmanızı öneririm .
wefwefa3

-4

Her zaman kullanmak daha iyidir c >= '0'çünkü c >= 48c'yi ascii kodunda dönüştürmeniz gerekir.


3
Bu cevap, bir hafta önceki önceki cevaplarda daha önce söylenmemiş olanı ne ifade ediyor?

-5

Normal İfadeler ( RegEx s) rakamlar için belirli bir karakter sınıfına sahiptir\d - bu, dizginizden başka bir karakteri kaldırmak için kullanılabilir. Elde edilen dizginin uzunluğu istenen değerdir.

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

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

Bununla birlikte, RegEx'lerin önerilen diğer çözümlerden hesaplamalı olarak daha fazla talepkar olduklarına dikkat edin, bu nedenle genel olarak tercih edilmemeleri gerekir .


Çek yapmak için çok şık bir yol!
Kevin Robatel

Regexes böyle bir görev için overkill
Pharap

2
@StefanoBragaglia Cevabınızı yeniden okuduktan sonra soruyu gerçekten cevaplamadığını düşünüyorum.
Pharap

2
Cevabınız "bir dizgede rakamları nasıl sayarım" problemini çözmenin farklı bir yolunu sunar. Temel örneklere kod örnekleri ve sabitlerin gösterimi - sayı veya karakter olarak cevap vermez.

2
Bu aslında rakamları saymaz (sadece ne burada ne de orada olmayan tüm basamakları çıkardıktan sonra dizenin uzunluğunun ne olduğunu gösterir), ancak soruyu aslında yanıtlamadığını kabul ediyorum. Mesela, kimse karakterleri karakterlerden silmeyi istemiyordu. Soru sadece bir karakterin sayısal olup olmadığını kontrol etmek için uygun en iyi uygulama yöntemini soruyor.
doppelgreener
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.