To rozwiązanie dopasuje ciągi o zerowej długości. użyj + zamiast *, aby dopasować ciągi składające się z 1 lub więcej znaków.
Jerub,
10
@Prestaul: \wobejmuje, \da _zatem isvalid = re.match(r'[\w-]+$', astr)lub isinvalid = re.search(r'[^\w-]', astr). Ewentualna obecność locale.setlocaleciągów znaków Unicode lub Unicode wymaga dodatkowego rozważenia.
jfs
1
Poprawka: isvalid = re.match(r'[\w-]*$', astr)- puste łańcuchy są ważne.
jfs
Jak możesz również dopuścić kropkę / kropkę (.) W tym wyrażeniu regularnym? Edytuj, oto jak: ^ [a-zA-Z0-9 -_ \ s \.] + $
fredrik,
24
[Edytuj] Jest jeszcze jedno rozwiązanie, o którym nie wspomniano, i wydaje się, że w większości przypadków przewyższa inne podane do tej pory.
Użyj string.translate, aby zastąpić wszystkie prawidłowe znaki w ciągu i sprawdź, czy nie zostały nam jakieś nieprawidłowe znaki. Jest to dość szybkie, ponieważ wykorzystuje podstawową funkcję C do wykonania pracy, z bardzo małą ilością kodu bajtowego Pythona.
Oczywiście wydajność to nie wszystko - wybranie najbardziej czytelnych rozwiązań jest prawdopodobnie najlepszym podejściem, gdy nie ma ścieżki kodowej krytycznej dla wydajności, ale aby zobaczyć, jak układają się rozwiązania, oto porównanie wydajności wszystkich zaproponowanych do tej pory metod. check_trans to ten, który używa metody string.translate.
Kod testowy:
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('','')
defcheck_set_diff(s):returnnot set(s) - allowed_set
defcheck_set_all(s):return all(x in allowed_set for x in s)
defcheck_set_subset(s):return set(s).issubset(allowed_set)
defcheck_re_match(s):return pat.match(s)
defcheck_re_inverse(s):# Search for non-matching character.returnnot pat_inv.search(s)
defcheck_trans(s):returnnot 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=''defmain():
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))
printif __name__=='__main__': main()
Podejście translate wydaje się najlepsze w większości przypadków, dramatycznie tak w przypadku długich prawidłowych ciągów, ale jest pokonane przez wyrażenia regularne w test_long_invalid (przypuszczalnie dlatego, że wyrażenie regularne może natychmiast się wycofać, ale translate zawsze musi skanować cały ciąg). Podejścia do zestawu są zwykle najgorsze, pokonując wyrażenia regularne tylko dla pustego przypadku łańcucha.
Użycie all (x in allowed_set dla x in s) działa dobrze, jeśli wcześniej kończy się niepowodzeniem, ale może być złe, jeśli musi iterować przez każdy znak. isSubSet i set różnica są porównywalne i są zawsze proporcjonalne do długości łańcucha, niezależnie od danych.
Podobna różnica występuje między metodami wyrażeń regularnych dopasowujących wszystkie prawidłowe znaki i wyszukiwaniem nieprawidłowych znaków. Dopasowywanie działa trochę lepiej, gdy sprawdza się długi, ale w pełni poprawny ciąg, ale gorzej w przypadku nieprawidłowych znaków pod koniec ciągu.
Użyj string.ascii_letterszamiast, string.lettersjeśli nie używasz flagi re.LOCALE dla wyrażeń regularnych (w przeciwnym razie możesz uzyskać fałszywie dodatnie wyniki w check_trans(). string.maketrans()Nie będzie działać dla ciągów znaków Unicode.
jfs
W przypadku Pythona 3 / Unicode / from __future__ import unicode_literals) użyj trans_table3 = dict((ord(char), '') for char in allowed_chars)i def check_trans(s): return not s.translate(trans_table3). Ale generalnie radzi sobie gorzej niż wersje RE.
Hugo,
14
Istnieje wiele sposobów osiągnięcia tego celu, niektóre są jaśniejsze niż inne. Dla każdego z moich przykładów „True” oznacza, że przekazany ciąg jest prawidłowy, „False” oznacza, że zawiera nieprawidłowe znaki.
Przede wszystkim istnieje naiwne podejście:
import string
allowed = string.letters + string.digits + '_' + '-'defcheck_naive(mystring):return all(c in allowed for c in mystring)
Następnie jest użycie wyrażenia regularnego, możesz to zrobić za pomocą re.match (). Zwróć uwagę, że „-” musi znajdować się na końcu [], w przeciwnym razie zostanie użyty jako separator „zakresu”. Zwróć także uwagę na znak $, który oznacza „koniec ciągu”. Inne odpowiedzi wymienione w tym pytaniu używają specjalnej klasy znaków, „\ w”, zawsze wolę używać wyraźnego zakresu klas znaków przy użyciu [], ponieważ jest to łatwiejsze do zrozumienia bez konieczności szukania skróconej instrukcji obsługi i łatwiejsze do specjalnego- walizka.
import re
CHECK_RE = re.compile('[a-zA-Z0-9_-]+$')
defcheck_re(mystring):return CHECK_RE.match(mystring)
W innym rozwiązaniu zauważono, że można wykonać odwrotne dopasowanie za pomocą wyrażeń regularnych, dodałem to teraz. Zauważ, że [^ ...] odwraca klasę znaków, ponieważ jest używany ^:
Możesz także zrobić coś trudnego z obiektem „set”. Spójrz na ten przykład, który usuwa z oryginalnego ciągu wszystkie dozwolone znaki, pozostawiając nam zestaw zawierający albo a) nic, albo b) obraźliwe znaki z ciągu:
W pierwszym teście wyrażenia regularnego nie powinno być „[a-zA-Z0-9 _-] + $” być „[a-zA-Z0-9 _-] * $”. Pusty ciąg powinien prawdopodobnie zostać uznany za zgodny.
Alternatywnie do używania wyrażenia regularnego możesz to zrobić w Ustawieniach:
from sets import Set
allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')
if Set(my_little_sting).issubset(allowed_chars):
# your actionprintTrue
import re;
re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w: Tylko [a-zA-Z0-9_]
Musisz więc dodać -znak, aby uzasadnić znak łącznika.
+: Dopasuj jedno lub więcej powtórzeń poprzedniego znaku. Chyba nie akceptujesz pustych danych wejściowych. Ale jeśli tak, zmień na *.
^: Dopasowuje początek łańcucha.
$: Dopasowuje koniec łańcucha.
Potrzebujesz tych dwóch znaków specjalnych, ponieważ musisz uniknąć następującego przypadku. Niechciane znaki, takie jak &tutaj, mogą pojawić się między dopasowanym wzorem.
Cóż, możesz poprosić o pomoc regex, świetny tutaj :)
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'
Zawsze możesz użyć funkcji rozumienia listy i sprawdzić wyniki ze wszystkimi, wymagałoby to trochę mniej zasobów niż użycie wyrażenia regularnego: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
Przetestuj swój kod przed wysłaniem. Rozwiązanie oparte na twojej zepsutej odpowiedzi, które działa, to: all (c in string.letters + string.digits + "_" zamiast c w mystring)
Jerub
2
To będzie dużo bardziej wymagające zasobów niż regex. Wykonuje liniowe skanowanie dla każdego znaku (lepiej jest zbudować zestaw z wyprzedzeniem) i niepotrzebnie tworzysz listę, gdy rozumienie generatora byłoby lżejsze.
Brian,
-1
Oto coś opartego na „naiwnym podejściu” Jeruba (naiwność to jego słowa, a nie moje!):
import string
ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-')
defcheck(mystring):return all(c in ALLOWED for c in mystring)
Jeśli byłby ALLOWEDciągiem, myślę, c in ALLOWEDże wymagałoby to iteracji po każdym znaku w ciągu, aż znalazłby dopasowanie lub osiągnął koniec. Który, cytując Joela Spolsky'ego, jest czymś w rodzaju algorytmu Shlemiel the Painter .
Ale testowanie istnienia w zestawie powinno być bardziej wydajne lub przynajmniej mniej zależne od liczby dozwolonych znaków. Z pewnością to podejście jest trochę szybsze na moim komputerze. Jest to jasne i myślę, że działa wystarczająco dobrze w większości przypadków (na moim wolnym komputerze mogę sprawdzić dziesiątki tysięcy krótkich ciągów w ułamku sekundy). Lubię to.
W rzeczywistości na moim komputerze wyrażenie regularne działa kilka razy szybciej i jest tak samo proste (prawdopodobnie prostsze). Więc to prawdopodobnie najlepsza droga naprzód.
Wszystkie te znaki muszą należeć do jednej klasy, w przeciwnym razie otrzymasz fałszywie ujemne wartości. Zapomniałeś również dołączyć znaczniki początku i końca łańcucha ... w ten sposób zawsze będzie pasować, o ile obecny jest jeden prawidłowy znak.
Thomas,
1
To faktycznie będzie pasować, nawet jeśli nie ma prawidłowych znaków. Dopasowanie o zerowej długości. Poza tym nie jest w Pythonie.
Odpowiedzi:
Wyrażenie regularne załatwi sprawę z bardzo małą ilością kodu:
import re ... if re.match("^[A-Za-z0-9_-]*$", my_little_string): # do something here
źródło
\w
obejmuje,\d
a_
zatemisvalid = re.match(r'[\w-]+$', astr)
lubisinvalid = re.search(r'[^\w-]', astr)
. Ewentualna obecnośćlocale.setlocale
ciągów znaków Unicode lub Unicode wymaga dodatkowego rozważenia.isvalid = re.match(r'[\w-]*$', astr)
- puste łańcuchy są ważne.[Edytuj] Jest jeszcze jedno rozwiązanie, o którym nie wspomniano, i wydaje się, że w większości przypadków przewyższa inne podane do tej pory.
Użyj string.translate, aby zastąpić wszystkie prawidłowe znaki w ciągu i sprawdź, czy nie zostały nam jakieś nieprawidłowe znaki. Jest to dość szybkie, ponieważ wykorzystuje podstawową funkcję C do wykonania pracy, z bardzo małą ilością kodu bajtowego Pythona.
Oczywiście wydajność to nie wszystko - wybranie najbardziej czytelnych rozwiązań jest prawdopodobnie najlepszym podejściem, gdy nie ma ścieżki kodowej krytycznej dla wydajności, ale aby zobaczyć, jak układają się rozwiązania, oto porównanie wydajności wszystkich zaproponowanych do tej pory metod. check_trans to ten, który używa metody string.translate.
Kod testowy:
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()
Wyniki w moim systemie to:
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
Podejście translate wydaje się najlepsze w większości przypadków, dramatycznie tak w przypadku długich prawidłowych ciągów, ale jest pokonane przez wyrażenia regularne w test_long_invalid (przypuszczalnie dlatego, że wyrażenie regularne może natychmiast się wycofać, ale translate zawsze musi skanować cały ciąg). Podejścia do zestawu są zwykle najgorsze, pokonując wyrażenia regularne tylko dla pustego przypadku łańcucha.
Użycie all (x in allowed_set dla x in s) działa dobrze, jeśli wcześniej kończy się niepowodzeniem, ale może być złe, jeśli musi iterować przez każdy znak. isSubSet i set różnica są porównywalne i są zawsze proporcjonalne do długości łańcucha, niezależnie od danych.
Podobna różnica występuje między metodami wyrażeń regularnych dopasowujących wszystkie prawidłowe znaki i wyszukiwaniem nieprawidłowych znaków. Dopasowywanie działa trochę lepiej, gdy sprawdza się długi, ale w pełni poprawny ciąg, ale gorzej w przypadku nieprawidłowych znaków pod koniec ciągu.
źródło
string.ascii_letters
zamiast,string.letters
jeśli nie używasz flagi re.LOCALE dla wyrażeń regularnych (w przeciwnym razie możesz uzyskać fałszywie dodatnie wyniki wcheck_trans()
.string.maketrans()
Nie będzie działać dla ciągów znaków Unicode.from __future__ import unicode_literals
) użyjtrans_table3 = dict((ord(char), '') for char in allowed_chars)
i defcheck_trans(s): return not s.translate(trans_table3)
. Ale generalnie radzi sobie gorzej niż wersje RE.Istnieje wiele sposobów osiągnięcia tego celu, niektóre są jaśniejsze niż inne. Dla każdego z moich przykładów „True” oznacza, że przekazany ciąg jest prawidłowy, „False” oznacza, że zawiera nieprawidłowe znaki.
Przede wszystkim istnieje naiwne podejście:
import string allowed = string.letters + string.digits + '_' + '-' def check_naive(mystring): return all(c in allowed for c in mystring)
Następnie jest użycie wyrażenia regularnego, możesz to zrobić za pomocą re.match (). Zwróć uwagę, że „-” musi znajdować się na końcu [], w przeciwnym razie zostanie użyty jako separator „zakresu”. Zwróć także uwagę na znak $, który oznacza „koniec ciągu”. Inne odpowiedzi wymienione w tym pytaniu używają specjalnej klasy znaków, „\ w”, zawsze wolę używać wyraźnego zakresu klas znaków przy użyciu [], ponieważ jest to łatwiejsze do zrozumienia bez konieczności szukania skróconej instrukcji obsługi i łatwiejsze do specjalnego- walizka.
import re CHECK_RE = re.compile('[a-zA-Z0-9_-]+$') def check_re(mystring): return CHECK_RE.match(mystring)
W innym rozwiązaniu zauważono, że można wykonać odwrotne dopasowanie za pomocą wyrażeń regularnych, dodałem to teraz. Zauważ, że [^ ...] odwraca klasę znaków, ponieważ jest używany ^:
CHECK_INV_RE = re.compile('[^a-zA-Z0-9_-]') def check_inv_re(mystring): return not CHECK_INV_RE.search(mystring)
Możesz także zrobić coś trudnego z obiektem „set”. Spójrz na ten przykład, który usuwa z oryginalnego ciągu wszystkie dozwolone znaki, pozostawiając nam zestaw zawierający albo a) nic, albo b) obraźliwe znaki z ciągu:
def check_set(mystring): return not set(mystring) - set(allowed)
źródło
string.ascii_letters
jeśli używasz wyrażeń regularnych „[a-zA-Z]”.Gdyby nie myślniki i podkreślenia, najłatwiejszym rozwiązaniem byłoby
(Sekcja 3.6.1 dokumentacji Python Library)
źródło
Alternatywnie do używania wyrażenia regularnego możesz to zrobić w Ustawieniach:
from sets import Set allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-') if Set(my_little_sting).issubset(allowed_chars): # your action print True
źródło
pat = re.compile ('[^\w-]') def onlyallowed(s): return not pat.search (s)
źródło
Wyrażenie regularne może być bardzo elastyczne.
import re; re.fullmatch("^[\w-]+$", target_string) # fullmatch looks also workable for python 3.4
\w
: Tylko[a-zA-Z0-9_]
Musisz więc dodać
-
znak, aby uzasadnić znak łącznika.+
: Dopasuj jedno lub więcej powtórzeń poprzedniego znaku. Chyba nie akceptujesz pustych danych wejściowych. Ale jeśli tak, zmień na*
.^
: Dopasowuje początek łańcucha.$
: Dopasowuje koniec łańcucha.Potrzebujesz tych dwóch znaków specjalnych, ponieważ musisz uniknąć następującego przypadku. Niechciane znaki, takie jak
&
tutaj, mogą pojawić się między dopasowanym wzorem.&&&PATTERN&&PATTERN
źródło
Cóż, możesz poprosić o pomoc regex, świetny tutaj :)
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'
Wynik:
Mam nadzieję że to pomoże :)
źródło
Zawsze możesz użyć funkcji rozumienia listy i sprawdzić wyniki ze wszystkimi, wymagałoby to trochę mniej zasobów niż użycie wyrażenia regularnego:
all([c in string.letters + string.digits + ["_", "-"] for c in mystring])
źródło
Oto coś opartego na „naiwnym podejściu” Jeruba (naiwność to jego słowa, a nie moje!):
import string ALLOWED = frozenset(string.ascii_letters + string.digits + '_' + '-') def check(mystring): return all(c in ALLOWED for c in mystring)
Jeśli byłby
ALLOWED
ciągiem, myślę,c in ALLOWED
że wymagałoby to iteracji po każdym znaku w ciągu, aż znalazłby dopasowanie lub osiągnął koniec. Który, cytując Joela Spolsky'ego, jest czymś w rodzaju algorytmu Shlemiel the Painter .Ale testowanie istnienia w zestawie powinno być bardziej wydajne lub przynajmniej mniej zależne od liczby dozwolonych znaków. Z pewnością to podejście jest trochę szybsze na moim komputerze. Jest to jasne i myślę, że działa wystarczająco dobrze w większości przypadków (na moim wolnym komputerze mogę sprawdzić dziesiątki tysięcy krótkich ciągów w ułamku sekundy). Lubię to.
W rzeczywistości na moim komputerze wyrażenie regularne działa kilka razy szybciej i jest tak samo proste (prawdopodobnie prostsze). Więc to prawdopodobnie najlepsza droga naprzód.
źródło
użyj wyrażenia regularnego i zobacz, czy pasuje!
([a-z][A-Z][0-9]\_\-)*
źródło