it-swarm-id.com

Bagaimana cara saya membuktikan bahwa string hanya berisi huruf, angka, garis bawah, dan garis putus-putus?

Saya tahu bagaimana melakukan ini jika saya mengulangi semua karakter dalam string tetapi saya mencari metode yang lebih elegan.

76
Ethan Post

Ekspresi reguler akan melakukan trik dengan kode yang sangat sedikit:

import re

...

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

[Sunting] Ada solusi lain yang belum disebutkan, dan tampaknya mengungguli yang lain yang diberikan sejauh ini dalam kebanyakan kasus.

Gunakan string.translate untuk mengganti semua karakter yang valid dalam string, dan lihat apakah kita memiliki yang tidak valid yang tersisa. Ini cukup cepat karena menggunakan fungsi C yang mendasarinya untuk melakukan pekerjaan, dengan bytecode python sangat sedikit yang terlibat.

Jelas kinerja bukanlah segalanya - mencari solusi yang paling mudah dibaca mungkin adalah pendekatan terbaik ketika tidak berada dalam codepath kritis kinerja, tetapi hanya untuk melihat bagaimana solusi menumpuk, inilah perbandingan kinerja semua metode yang diusulkan sejauh ini. check_trans adalah yang menggunakan metode string.translate.

Kode uji:

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

Hasil pada sistem saya adalah:

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

Pendekatan penerjemahan tampaknya paling baik dalam banyak kasus, secara dramatis demikian dengan string yang panjang dan valid, tetapi dikalahkan oleh regex di test_long_invalid (Mungkin karena regex dapat segera menebus, tetapi menerjemahkan selalu harus memindai seluruh string). Pendekatan himpunan biasanya terburuk, mengalahkan regex hanya untuk kasus string kosong.

Menggunakan semua (x di allow_set untuk x dalam s) berkinerja baik jika menebus lebih awal, tetapi bisa buruk jika harus mengulangi setiap karakter. isSubSet dan perbedaan set sebanding, dan secara konsisten sebanding dengan panjang string terlepas dari data.

Ada perbedaan yang serupa antara metode regex yang cocok dengan semua karakter yang valid dan mencari karakter yang tidak valid. Pencocokan berkinerja lebih baik ketika memeriksa string yang panjang, tetapi sepenuhnya valid, tetapi lebih buruk untuk karakter yang tidak valid di dekat akhir string.

22
Brian

Ada berbagai cara untuk mencapai tujuan ini, ada yang lebih jelas daripada yang lain. Untuk masing-masing contoh saya, 'Benar' berarti bahwa string yang dikirimkan adalah benar, 'Salah' artinya berisi karakter yang tidak valid.

Pertama-tama, ada pendekatan naif:

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

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

Lalu ada penggunaan ekspresi reguler, Anda bisa melakukan ini dengan re.match (). Perhatikan bahwa '-' harus di akhir [] jika tidak maka akan digunakan sebagai pembatas 'rentang'. Perhatikan juga $ yang berarti 'akhir dari string'. Jawaban lain yang dicatat dalam pertanyaan ini menggunakan kelas karakter khusus, '\ w', saya selalu lebih suka menggunakan rentang kelas karakter eksplisit menggunakan [] karena lebih mudah dipahami tanpa harus mencari panduan referensi cepat, dan lebih mudah untuk kasus.

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

Solusi lain mencatat bahwa Anda dapat melakukan kecocokan terbalik dengan ekspresi reguler, saya sudah memasukkannya di sini sekarang. Perhatikan bahwa [^ ...] membalikkan kelas karakter karena ^ digunakan:

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

Anda juga dapat melakukan sesuatu yang rumit dengan objek 'set'. Lihat contoh ini, yang menghapus dari string asli semua karakter yang diizinkan, meninggalkan kita dengan set yang mengandung a) tidak ada, atau b) karakter yang menyinggung dari string:

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

Jika bukan karena garis putus-putus dan garis bawah, solusi termudah adalah

my_little_string.isalnum()

(Bagian 3.6.1 dari Referensi Python Library)

11
Ber

Sebagai alternatif untuk menggunakan regex Anda bisa melakukannya di Sets:

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

Nah Anda bisa meminta bantuan regex, hebat di sini :)

kode:

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'

Keluaran:

yes  

Semoga ini membantu :)

1

Anda selalu bisa menggunakan daftar pemahaman dan memeriksa hasilnya dengan semua, itu akan menjadi sumber daya sedikit kurang intensif daripada menggunakan regex: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])

0
William Keller

Ekspresi reguler bisa sangat fleksibel. 

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

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

Jadi, Anda perlu menambahkan char -.

+: Cocokkan satu atau lebih pengulangan char sebelumnya. Saya kira Anda tidak menerima input kosong. Tetapi jika Anda melakukannya, ubah ke *.

^: Cocok dengan awal string.

$: Cocok dengan akhir string.

Anda memerlukan dua karakter khusus ini karena Anda harus menghindari kasus berikut:

&&&PATTERN&&PATTERN

Pola yang tidak Anda inginkan mungkin duduk di antara pola yang Anda inginkan. 

Untuk contoh ini: &&& bukan kasus yang Anda harapkan tetapi string legal dapat diterima. Jika Anda tidak menambahkan ^ dan $ ke ekspresi reguler, maka pola ini akan cocok dengan pola yang salah.

0
Alston

Berikut ini sesuatu yang didasarkan pada "pendekatan naif" Jerub (naif menjadi kata-katanya, bukan milikku!):

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

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

Jika ALLOWED adalah string maka saya pikir c in ALLOWED akan melibatkan iterasi setiap karakter dalam string hingga ditemukan kecocokan atau mencapai akhir. Yang, mengutip Joel Spolsky, adalah sesuatu dari Shlemiel algoritma Painter .

Tetapi pengujian keberadaan dalam satu set harus lebih efisien, atau setidaknya kurang tergantung pada jumlah karakter yang diperbolehkan. Tentu saja pendekatan ini sedikit lebih cepat di komputer saya. Sudah jelas dan saya pikir kinerjanya cukup baik untuk kebanyakan kasus (pada mesin lambat saya, saya dapat memvalidasi puluhan ribu string pendek dalam sepersekian detik). Saya suka itu.

SEBENARNYA pada komputer saya regexp bekerja beberapa kali lebih cepat, dan sesederhana ini (bisa dibilang lebih sederhana). Jadi itu mungkin cara terbaik ke depan.

0
MB.