Bir dizenin yalnızca harf, rakam, alt çizgi ve çizgi içerdiğini nasıl doğrularım?


87

Dizedeki tüm karakterleri yinelediğimde bunu nasıl yapacağımı biliyorum ama daha zarif bir yöntem arıyorum.


5
Ascii, yerel ayara özgü veya unicode harflerden mi bahsediyorsunuz?
jfs

Yanıtlar:


124

Normal bir ifade, çok az kodla hile yapacaktır:

import re

...

if re.match("^[A-Za-z0-9_-]*$", my_little_string):
    # do something here

25
Bunu şu şekilde basitleştirebilirsiniz: ^ [\ w \ d _-] * $
Prestaul

13
Bu çözüm, sıfır uzunluktaki dizelerle eşleşecektir. 1 veya daha fazla karakterden oluşan dizelerle eşleşmesini sağlamak için * yerine + kullanın.
Jerub

10
@Prestaul: \wiçerir \dve _bu nedenle isvalid = re.match(r'[\w-]+$', astr)veya isinvalid = re.search(r'[^\w-]', astr). Olası bir locale.setlocaleunicode dizesi varlığı, ek değerlendirme gerektirir.
jfs

1
Düzeltme: isvalid = re.match(r'[\w-]*$', astr)- boş dizeler geçerlidir.
jfs

Bu normal ifadede bir noktaya / noktaya (.) Da nasıl izin verebilirsiniz? Düzenleme, burada nasıl: ^ + $ [a-zA-Z0-9 -_ \ s \.]
fredrik

25

[Düzenle] Henüz bahsedilmeyen başka bir çözüm var ve çoğu durumda şimdiye kadar verilen diğerlerinden daha iyi performans gösteriyor gibi görünüyor.

Dizedeki tüm geçerli karakterleri değiştirmek için string.translate kullanın ve geriye kalan geçersiz karakterler olup olmadığına bakın. Çok az python bayt kodu dahil olmak üzere, işi yapmak için temeldeki C işlevini kullandığı için bu oldukça hızlıdır.

Açıkçası performans her şey değildir - en okunabilir çözümlere gitmek, performans açısından kritik bir kod yolunda olmadığında muhtemelen en iyi yaklaşımdır, ancak çözümlerin nasıl yığıldığını görmek için, şimdiye kadar önerilen tüm yöntemlerin performans karşılaştırmasını burada bulabilirsiniz. check_trans, string.translate yöntemini kullanan olandır.

Test kodu:

import string, re, timeit

pat = re.compile('[\w-]*$')
pat_inv = re.compile ('[^\w-]')
allowed_chars=string.ascii_letters + string.digits + '_-'
allowed_set = set(allowed_chars)
trans_table = string.maketrans('','')

def check_set_diff(s):
    return not set(s) - allowed_set

def check_set_all(s):
    return all(x in allowed_set for x in s)

def check_set_subset(s):
    return set(s).issubset(allowed_set)

def check_re_match(s):
    return pat.match(s)

def check_re_inverse(s): # Search for non-matching character.
    return not pat_inv.search(s)

def check_trans(s):
    return not s.translate(trans_table,allowed_chars)

test_long_almost_valid='a_very_long_string_that_is_mostly_valid_except_for_last_char'*99 + '!'
test_long_valid='a_very_long_string_that_is_completely_valid_' * 99
test_short_valid='short_valid_string'
test_short_invalid='/$%$%&'
test_long_invalid='/$%$%&' * 99
test_empty=''

def main():
    funcs = sorted(f for f in globals() if f.startswith('check_'))
    tests = sorted(f for f in globals() if f.startswith('test_'))
    for test in tests:
        print "Test %-15s (length = %d):" % (test, len(globals()[test]))
        for func in funcs:
            print "  %-20s : %.3f" % (func, 
                   timeit.Timer('%s(%s)' % (func, test), 'from __main__ import pat,allowed_set,%s' % ','.join(funcs+tests)).timeit(10000))
        print

if __name__=='__main__': main()

Sistemimdeki sonuçlar:

Test test_empty      (length = 0):
  check_re_inverse     : 0.042
  check_re_match       : 0.030
  check_set_all        : 0.027
  check_set_diff       : 0.029
  check_set_subset     : 0.029
  check_trans          : 0.014

Test test_long_almost_valid (length = 5941):
  check_re_inverse     : 2.690
  check_re_match       : 3.037
  check_set_all        : 18.860
  check_set_diff       : 2.905
  check_set_subset     : 2.903
  check_trans          : 0.182

Test test_long_invalid (length = 594):
  check_re_inverse     : 0.017
  check_re_match       : 0.015
  check_set_all        : 0.044
  check_set_diff       : 0.311
  check_set_subset     : 0.308
  check_trans          : 0.034

Test test_long_valid (length = 4356):
  check_re_inverse     : 1.890
  check_re_match       : 1.010
  check_set_all        : 14.411
  check_set_diff       : 2.101
  check_set_subset     : 2.333
  check_trans          : 0.140

Test test_short_invalid (length = 6):
  check_re_inverse     : 0.017
  check_re_match       : 0.019
  check_set_all        : 0.044
  check_set_diff       : 0.032
  check_set_subset     : 0.037
  check_trans          : 0.015

Test test_short_valid (length = 18):
  check_re_inverse     : 0.125
  check_re_match       : 0.066
  check_set_all        : 0.104
  check_set_diff       : 0.051
  check_set_subset     : 0.046
  check_trans          : 0.017

Çeviri yaklaşımı çoğu durumda en iyi gibi görünür, uzun geçerli dizelerde dramatik olarak böyledir, ancak test_long_invalid'deki normal ifadeler tarafından dövülür (Muhtemelen normal ifadenin hemen kurtarılabilmesi, ancak çevirinin her zaman tüm dizeyi taraması gerektiğinden). Küme yaklaşımları genellikle en kötüsüdür, normal ifadeleri yalnızca boş dize durumu için yener.

Tümünü (x in allow_set for x in s) kullanmak, erken çıkarsa iyi sonuç verir, ancak her karakteri yinelemesi gerekiyorsa kötü olabilir. isSubSet ve küme farkı karşılaştırılabilir ve verilerden bağımsız olarak dizenin uzunluğuyla tutarlı bir şekilde orantılıdır.

Tüm geçerli karakterlerle eşleşen ve geçersiz karakterleri arama normal ifade yöntemleri arasında benzer bir fark vardır. Eşleştirme, uzun, ancak tamamen geçerli bir dizeyi kontrol ederken biraz daha iyi performans gösterir, ancak dizenin sonuna yakın geçersiz karakterler için daha kötüdür.


1
Normal ifadeler için re.LOCALE bayrağını kullanmıyorsanız string.ascii_lettersbunun yerine kullanın string.letters(aksi takdirde içinde yanlış pozitif sonuçlar alabilirsiniz check_trans(). string.maketrans()Unicode dizeleri için çalışmayacaktır.
jfs

1
Python 3 / Unicode / from __future__ import unicode_literals) için kullanın trans_table3 = dict((ord(char), '') for char in allowed_chars)ve tanımlayın check_trans(s): return not s.translate(trans_table3). Ancak genel olarak RE versiyonlarından daha kötü performans gösterir.
Hugo

15

Bu hedefe ulaşmanın çeşitli yolları vardır, bazıları diğerlerinden daha nettir. Örneklerimin her biri için 'True', aktarılan dizenin geçerli olduğu, 'False' ise geçersiz karakterler içerdiği anlamına gelir.

Her şeyden önce saf bir yaklaşım var:

import string
allowed = string.letters + string.digits + '_' + '-'

def check_naive(mystring):
    return all(c in allowed for c in mystring)

Sonra bir normal ifade kullanımı var, bunu re.match () ile yapabilirsiniz. '-' öğesinin [] işaretinin sonunda olması gerektiğine dikkat edin, aksi takdirde bir 'aralık' sınırlayıcı olarak kullanılacaktır. Ayrıca 'dizgenin sonu' anlamına gelen $ 'a da dikkat edin. Bu soruda belirtilen diğer yanıtlar özel bir karakter sınıfı, '\ w' kullanır, ben her zaman [] kullanarak açık bir karakter sınıfı aralığı kullanmayı tercih ederim çünkü hızlı bir başvuru kılavuzuna bakmak zorunda kalmadan anlamak daha kolaydır ve özelden daha kolaydır durum.

import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
def check_re(mystring):
    return CHECK_RE.match(mystring)

Başka bir çözüm, normal ifadelerle ters eşleşme yapabileceğinizi belirtti, bunu şimdi buraya ekledim. [^ ...] karakter sınıfını ters çevirir çünkü ^ kullanılır:

CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]')
def check_inv_re(mystring):
   return not CHECK_INV_RE.search(mystring)

Ayrıca 'set' nesnesiyle zor bir şey de yapabilirsiniz. Orijinal dizeden izin verilen tüm karakterleri kaldıran ve bize a) hiçbir şey içermeyen veya b) dizeden rahatsız edici karakterleri içeren bir küme bırakan bu örneğe bir göz atın:

def check_set(mystring):
    return not set(mystring) - set(allowed)

İlk normal ifade testinizde "[a-zA-Z0-9 _-] + $" "[a-zA-Z0-9 _-] * $" olmamalıdır. Boş dizge muhtemelen eşleşme olarak değerlendirilmelidir.
Brian

Kullan string.ascii_letterssen '[a-zA-Z]' İfadelerinin kullanırsanız.
jfs


4

Normal ifadeyi kullanmaya alternatif olarak bunu Kümeler'de yapabilirsiniz:

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True

3
 pat = re.compile ('[^\w-]')

 def onlyallowed(s):
    return not pat.search (s)

1

Düzenli ifade çok esnek olabilir.

import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4

\w: Bir tek [a-zA-Z0-9_]

Bu nedenle, -kısa çizgi karakterini yaslamak için karakter eklemeniz gerekir .

+: Önceki karakterin bir veya daha fazla tekrarını eşleştirin. Sanırım boş girişi kabul etmiyorsunuz. Ama yaparsan, olarak değiştir *.

^: Dizenin başlangıcıyla eşleşir.

$: Dizenin sonuyla eşleşir.

Aşağıdaki durumdan kaçınmanız gerektiğinden bu iki özel karaktere ihtiyacınız var. Buradaki gibi istenmeyen karakterler &, eşleşen desen arasında görünebilir.

&&&PATTERN&&PATTERN


0

Pekala, regex'in yardımını isteyebilirsin, buradaki harika :)

kod:

import re

string = 'adsfg34wrtwe4r2_()' #your string that needs to be matched.
regex = r'^[\w\d_()]*$' # you can also add a space in regex if u want to allow it in the string  
if re.match(regex,string):
    print 'yes'
else: 
    print 'false'

Çıktı:

yes  

Bu yardımcı olur umarım :)


-1

Her zaman bir liste anlama kullanabilir ve sonuçları hepsiyle kontrol edebilirsiniz, bir normal ifade kullanmaktan biraz daha az kaynak yoğun olacaktır: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])


Lütfen kodunuzu göndermeden önce test edin. Çalışan bozuk cevabınıza dayalı bir çözüm şudur: tümü (dizgimdeki c için string.letters + string.digits + "_" c in
Jerub

2
Bu , bir normal ifadeden çok daha fazla kaynak gerektirir. Her karakter için doğrusal bir tarama yapıyor (önceden bir set oluşturmak daha iyidir) ve bir üreteç anlayışının daha hafif olacağı durumlarda gereksiz yere bir liste oluşturuyorsunuz.
Brian

-1

İşte Jerub'un "saf yaklaşımı" na dayanan bir şey (onun sözleri saf, benim değil!):

import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')

def check(mystring):
    return all(c in ALLOWED for c in mystring)

Eğer ALLOWEDbir dize oldu sonra düşünüyorum c in ALLOWEDda bir maç bulunamadı veya sonuna ulaşana kadar dizedeki her karakterin üzerinde iterating yer alacak. Joel Spolsky'den alıntı yapacak olursak, Shlemiel the Painter algoritması gibi .

Ancak bir kümede var olup olmadığını test etmek daha verimli olmalı veya en azından izin verilen karakter sayısına daha az bağlı olmalıdır. Elbette bu yaklaşım benim makinemde biraz daha hızlı. Açıktır ve çoğu durum için yeterince iyi performans gösterdiğini düşünüyorum (yavaş makinemde onbinlerce kısa telli dizgiyi bir saniyeden kısa sürede doğrulayabilirim). Bunu sevdim.

GERÇEKTEN , makinemde bir regexp birkaç kat daha hızlı çalışıyor ve bu kadar basit (muhtemelen daha basit). Yani bu muhtemelen ilerlemenin en iyi yoludur.


-4

bir normal ifade kullanın ve eşleşip eşleşmediğine bakın!

([a-z][A-Z][0-9]\_\-)*

1
Tüm bu karakterler tek bir sınıfta olmalıdır, yoksa yanlış negatifler alırsınız. Ayrıca dizenin başlangıcı ve dizge sonu belirteçlerini eklemeyi unuttunuz ... bunun gibi, geçerli bir karakter olduğu sürece her zaman eşleşecektir.
Thomas

1
Geçerli karakterler olmasa bile bu aslında eşleşecektir. Sıfır uzunlukta eşleşme. Ayrıca, python'da değil.
Jerub
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.