Подтвердить что ты не робот

Как проверить, что строка содержит только буквы, цифры, символы подчеркивания и тире?

Я знаю, как это сделать, если я повторяю все символы в строке, но я ищу более элегантный метод.

4b9b3361

Ответ 1

Регулярное выражение сделает трюк с очень маленьким кодом:

import re

...

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

Ответ 2

[Edit] Там еще не упомянуто еще одно решение, и оно, по-видимому, превосходит остальных, которые даны до сих пор в большинстве случаев.

Используйте string.translate, чтобы заменить все допустимые символы в строке, и посмотрите, остались ли у нас недопустимые. Это довольно быстро, так как он использует базовую функцию C для выполнения этой работы, при этом задействовано малое байт-код python.

Очевидно, что производительность - это еще не все: для наиболее читаемых решений, вероятно, лучший подход, когда не в критическом кодовом коде, а просто для того, чтобы увидеть, как складываются решения, здесь сравнивается производительность всех предложенных методов, check_trans - тот, который использует метод string.translate.

Тестовый код:

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

Результаты моей системы:

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

В большинстве случаев подход перевода выглядит лучше всего, что очень важно с длинными действительными строками, но выбивается с помощью регулярных выражений в test_long_invalid (предположительно потому, что регулярное выражение может сразу же выручить, но переводить всегда приходится сканировать всю строку). Установленные подходы, как правило, худшие, избиение регулярных выражений только для случая пустой строки.

Использование всех (x в разрешенном_set для x в s) хорошо работает, если оно выдается раньше, но может быть плохим, если оно должно проходить через каждый символ. isSubSet и заданные различия сопоставимы и последовательно пропорциональны длине строки независимо от данных.

Существует аналогичное различие между методами регулярного выражения, соответствующими всем допустимым символам и поиском недопустимых символов. Сравнение выполняется немного лучше при проверке длинной, но полностью действующей строки, но хуже для недопустимых символов в конце строки.

Ответ 3

Существует множество способов достижения этой цели, некоторые из них более ясны, чем другие. Для каждого из моих примеров "True" означает, что переданная строка действительна, "False" означает, что она содержит недопустимые символы.

Прежде всего, существует наивный подход:

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

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

Затем используется регулярное выражение, вы можете сделать это с помощью функции re.match(). Обратите внимание, что '-' должно быть в конце [], иначе оно будет использоваться как разделитель диапазона. Также обратите внимание на $, что означает "конец строки". В других ответах, отмеченных в этом вопросе, используется специальный класс символов "\ w", я всегда предпочитаю использовать явный диапазон классов символов с помощью [], потому что его легче понять без необходимости искать краткое справочное руководство и, случай.

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

Другое решение отметило, что вы можете сделать обратное совпадение с регулярными выражениями, я включил это здесь. Обратите внимание, что [^...] инвертирует класс символов, потому что используется ^:

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

Вы также можете сделать что-то сложное с объектом "set". Посмотрите на этот пример, который удаляет из исходной строки все допустимые символы, оставляя нас с набором, содержащим либо a) ничего, либо b) оскорбительные символы из строки:

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

Ответ 4

Если бы не тире и подчеркивания, самым простым решением было бы

my_little_string.isalnum()

(Раздел 3.6.1 в справочнике библиотеки Python)

Ответ 5

В качестве альтернативы использованию регулярного выражения вы можете сделать это в Sets:

from sets import Set

allowed_chars = Set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-')

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

Ответ 6

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

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

Ответ 7

Ну, вы можете попросить помощь regex, великого здесь:)

код:

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'

Вывод:

yes  

Надеюсь, что это поможет:)

Ответ 8

Регулярное выражение может быть очень гибким.

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

\w: только [a-zA-Z0-9_]

Так что вам нужно добавить - char для оправдания дефиса char.

+: Сопоставить одно или несколько повторений предыдущего символа. Я думаю, вы не принимаете пустой ввод. Но если вы это сделаете, измените на *.

^: Соответствует началу строки.

$: Соответствует концу строки.

Вам нужны эти два специальных символа, поскольку вам нужно избегать следующего случая. Нежелательные символы, такие как & могут появиться между подходящим шаблоном.

&&&PATTERN&&PATTERN

Ответ 9

Вы всегда можете использовать понимание списка и проверять результаты со всеми, это будет немного менее ресурсоемким, чем использование регулярного выражения: all([c in string.letters + string.digits + ["_", "-"] for c in mystring])

Ответ 10

Здесь что-то, основанное на "наивном подходе" Иеруба (наивное - это его слова, а не мои!):

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

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

Если ALLOWED была строкой, я думаю, что c in ALLOWED будет включать итерацию по каждому символу в строке до тех пор, пока не найдет совпадение или не достигнет конца. Который, цитируя Джоэла Спольского, является чем-то вроде алгоритмом Шлемиеля Painter.

Но тестирование на существование в наборе должно быть более эффективным или, по крайней мере, менее зависимым от количества допустимых символов. Конечно, этот подход немного быстрее на моей машине. Это ясно, и я думаю, что он выполняет достаточно много для большинства случаев (на моей медленной машине я могу проверять десятки тысяч коротких строк за долю секунды). Мне это нравится.

ACTUALLY на моей машине регулярное выражение работает в несколько раз быстрее и так же просто, как это (возможно, проще). Так что, вероятно, это лучший способ продвижения вперед.

Ответ 11

используйте регулярное выражение и посмотрите, соответствует ли оно!

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