it-swarm-eu.dev

Jak ověřím, že řetězec obsahuje pouze písmena, čísla, podtržítka a pomlčky?

Vím, jak to udělat, když opakuji všechny postavy v řetězci, ale hledám elegantnější metodu.

76
Ethan Post

Regulární výraz udělá trik s velmi malým kódem:

import re

...

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

[Editovat] Existuje ještě jiné řešení, které ještě není zmíněno, a zdá se, že ve většině případů překonává ostatní.

Chcete-li nahradit všechny platné znaky v řetězci, použijte řetězec.translate a zjistěte, zda zůstaly nějaké neplatné znaky. To je velmi rychlé, protože používá základní funkce C, aby práci, s velmi malým zapojeným bytecode python.

Výkon samozřejmě není všechno - jít o nejčitelnější řešení je pravděpodobně nejlepším přístupem, když není v kritické oblasti výkonu, ale jen proto, aby se zjistilo, jak se tato řešení stohují, zde je srovnání všech navržených metod. check_trans je ten, který používá metodu string.translate.

Testovací kód:

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()

Výsledky mého systému jsou:

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

Přechodný přístup se jeví jako nejlepší ve většině případů, dramaticky tak s dlouhými platnými řetězci, ale je poražen regexy v test_long_invalid (Pravděpodobně proto, že regex může okamžitě vyléčit, ale překládat vždy musí skenovat celý řetězec). Nastavené přístupy jsou obvykle nejhorší, porážejí regexy pouze pro případ prázdného řetězce.

Používání všech (x v povolené_sloupce pro x v s) funguje dobře, pokud vyprší včas, ale může být špatné, pokud musí iterovat každou postavou. isSubSet a nastavený rozdíl jsou srovnatelné a jsou konzistentně úměrné délce řetězce bez ohledu na data.

Existuje podobný rozdíl mezi metodami regex, které odpovídají všem platným znakům a hledají neplatné znaky. Při kontrole dlouhého, ale plně platného řetězce, ale horšího pro neplatné znaky v blízkosti konce řetězce, probíhá párování lépe.

22
Brian

Existuje mnoho způsobů, jak tohoto cíle dosáhnout, jiné jsou jasnější než jiné. Pro každý z mých příkladů znamená 'True', že řetězec je platný, 'False' znamená, že obsahuje neplatné znaky.

Především je tu naivní přístup:

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

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

Pak se použije regulární výraz, můžete to provést pomocí re.match (). Všimněte si, že '-' musí být na konci [], jinak bude použit jako oddělovač rozsahu. Všimněte si také $, což znamená 'konec řetězce'. Další odpovědi uvedené v této otázce používají speciální znakovou třídu, '' ', vždy dávám přednost použití explicitní řady znaků pomocí [], protože je snazší jej pochopit, aniž by bylo nutné vyhledat rychlou referenční příručku případ.

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

Další řešení si všimlo, že můžete udělat inverzní zápas s regulárními výrazy, které jsem sem zahrnula. Všimněte si, že [^ ...] invertuje třídu znaků, protože se používá znak ^:

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

S objektem 'set' můžete také udělat něco složitého. Podívejte se na tento příklad, který z původního řetězce odstraní všechny znaky, které jsou povoleny, a ponecháme nám soubor obsahující buď a) nic, nebo b) znaky, které se v textu vyskytují:

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

Pokud by to nebylo pro pomlčky a podtržítka, nejjednodušší řešení by bylo

my_little_string.isalnum()

(Sekce 3.6.1 Knihovny Python)

11
Ber

Jako alternativu k použití regex to můžete provést v sadách:

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

if Set(my_little_sting).issubset(allowed_chars):
    # your action
    print True
5
Ber
 pat = re.compile ('[^\w-]')

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

No, můžete požádat o pomoc regex, skvělý tady :)

kód:

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'

Výstup:

yes  

Snad to pomůže :)

1

Vždy byste mohli používat seznamové porozumění a kontrolovat výsledky se všemi, bylo by to o něco méně náročné na zdroje než použití regex: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])

0
William Keller

Regulární výraz může být velmi flexibilní. 

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

\w: Pouze [a-zA-Z0-9_]

Takže musíte přidat - char.

+: Shoda jednoho nebo více opakování předchozího znaku. Myslím, že nepřijímáte prázdný vstup. Pokud ano, změňte na *.

^: Odpovídá začátku řetězce.

$: Odpovídá konci řetězce.

Tyto dva speciální znaky potřebujete, protože se musíte vyhnout následujícímu případu:

&&&PATTERN&&PATTERN

Vzor, který nechcete, pravděpodobně sedí mezi vzory, které chcete. 

Pro tuto instanci: &&& není případ, který očekáváte, ale řetězec legal je přijatelný. Pokud do regulárního výrazu nepřidáte ^ a $, bude tento vzor odpovídat nesprávnému vzoru.

0
Alston