Care este cel mai eficient mod în Python pentru a converti un șir de caractere pentru litere mici de separare toate caracterele alfa non-ascii?

voturi
27

Am o sarcină simplă am nevoie pentru a efectua în Python, care este de a converti un șir de caractere la litere mici și benzi din toate caracterele non-alfa non-ascii.

De exemplu:

This is a Test -> thisisatest
A235th@#$&( er Ra{}|?>ndom -> atherrandom

Am o funcție simplă de a face acest lucru:

import string
import sys

def strip_string_to_lowercase(s):
    tmpStr = s.lower().strip()
    retStrList = []
    for x in tmpStr:
        if x in string.ascii_lowercase:
            retStrList.append(x)

    return ''.join(retStrList)

Dar nu pot să mă gândesc există o mai eficientă, sau mai elegant, un fel.

Mulțumiri!


Editați | ×:

Vă mulțumim tuturor celor care au răspuns. Am învățat, și, în unele cazuri, re-învățat, o afacere bună de piton.

Întrebat 12/03/2009 la 13:35
sursa de către utilizator
În alte limbi...                            


11 răspunsuri

voturi
8

Aş:

  • șirul de litere mici
  • înlocui toate [^a-z]cu""

Ca asta:

def strip_string_to_lowercase():
  nonascii = re.compile('[^a-z]')
  return lambda s: nonascii.sub('', s.lower().strip())

EDIT: Se pare că versiunea originală (mai jos) este foarte lent, deși unele de performanță poate fi dobândită prin transformarea acestuia într-un sistem de închidere (de mai sus).

def strip_string_to_lowercase(s):
  return re.sub('[^a-z]', '', s.lower().strip())

Măsurătorile mele de performanță cu 100.000 de iterații împotriva șir

"A235th@#$&( er Ra{}|?>ndom"

a arătat că:

De dragul testului, nu am printrezultatele.

Publicat 12/03/2009 la 13:39
sursa de către utilizator

voturi
11

Nu în special în momentul rulării eficiente, dar cu siguranță mai plăcut pe ochi săraci, coder obosit:

def strip_string_and_lowercase(s):
    return ''.join(c for c in s.lower() if c in string.ascii_lowercase)
Publicat 12/03/2009 la 13:39
sursa de către utilizator

voturi
2
>>> import string
>>> a = "O235th@#$&( er Ra{}|?<ndom"
>>> ''.join(i for i in a.lower() if i in string.ascii_lowercase)
'otheraltndom'

face, în esență, la fel ca tine.

Publicat 12/03/2009 la 13:42
sursa de către utilizator

voturi
2

Aceasta este o aplicație tipică a listei compehension:

import string
s = "O235th@#$&( er Ra{}|?<ndom"
print ''.join(c for c in s.lower() if c in string.ascii_lowercase)

Nu va filtra „<“ (entitate html), la fel ca în exemplul vostru, dar presupun că a fost tăiat accidental și probleme din trecut.

Publicat 12/03/2009 la 13:43
sursa de către utilizator

voturi
0

Personal, aș folosi o expresie regulată și apoi converti șirul final cu litere mici. Nu am nici o idee cum să-l scrie în Python, dar ideea de bază este de a:

  1. Eliminați caractere în șir de caractere care nu se potrivesc cu regex sensibile la majuscule „ \w
  2. Conversia string la litere mici

sau vice versa.

Publicat 12/03/2009 la 13:44
sursa de către utilizator

voturi
4

Similar cu @ Dana, dar cred că acest lucru sună ca un loc de muncă de filtrare, și că ar trebui să fie vizibile în codul. De asemenea , fără a fi nevoie de a apela în mod explicit join():

def strip_string_to_lowercase(s):
  return filter(lambda x: x in string.ascii_lowercase, s.lower())
Publicat 12/03/2009 la 13:44
sursa de către utilizator

voturi
17
>>> filter(str.isalpha, "This is a Test").lower()
'thisisatest'
>>> filter(str.isalpha, "A235th@#$&( er Ra{}|?>ndom").lower()
'atherrandom'
Publicat 12/03/2009 la 14:55
sursa de către utilizator

voturi
24

O altă soluție (nu că pythonic, dar foarte rapid) este de a utiliza string.translate - deși nota că acest lucru nu va funcționa pentru unicode. Este , de asemenea , demn de remarcat faptul că puteți accelera codul Danei prin mutarea personajele într - un set (care se uită în sus de hash, mai degrabă decât să efectuați o căutare liniară de fiecare dată). Aici sunt temporizările am avea diverse din soluțiile date:

import string, re, timeit

# Precomputed values (for str_join_set and translate)

letter_set = frozenset(string.ascii_lowercase + string.ascii_uppercase)
tab = string.maketrans(string.ascii_lowercase + string.ascii_uppercase,
                       string.ascii_lowercase * 2)
deletions = ''.join(ch for ch in map(chr,range(256)) if ch not in letter_set)

s="A235th@#$&( er Ra{}|?>ndom"

# From unwind's filter approach
def test_filter(s):
    return filter(lambda x: x in string.ascii_lowercase, s.lower())

# using set instead (and contains)
def test_filter_set(s):
    return filter(letter_set.__contains__, s).lower()

# Tomalak's solution
def test_regex(s):
    return re.sub('[^a-z]', '', s.lower())

# Dana's
def test_str_join(s):
    return ''.join(c for c in s.lower() if c in string.ascii_lowercase)

# Modified to use a set.
def test_str_join_set(s):
    return ''.join(c for c in s.lower() if c in letter_set)

# Translate approach.
def test_translate(s):
    return string.translate(s, tab, deletions)


for test in sorted(globals()):
    if test.startswith("test_"):
        assert globals()[test](s)=='atherrandom'
        print "%30s : %s" % (test, timeit.Timer("f(s)", 
              "from __main__ import %s as f, s" % test).timeit(200000))

Acest lucru îmi dă:

               test_filter : 2.57138351271
           test_filter_set : 0.981806765698
                test_regex : 3.10069885233
             test_str_join : 2.87172979743
         test_str_join_set : 2.43197956381
            test_translate : 0.335367566218

[Edit] Ultima actualizare cu soluții de filtrare , precum și. (Rețineți că utilizarea set.__contains__face o mare diferenta aici, deoarece evită efectuarea unui apel în plus funcția pentru lambda.

Publicat 12/03/2009 la 15:06
sursa de către utilizator

voturi
3

Am adăugat soluțiile de filtrare la codul lui Brian:

import string, re, timeit

# Precomputed values (for str_join_set and translate)

letter_set = frozenset(string.ascii_lowercase + string.ascii_uppercase)
tab = string.maketrans(string.ascii_lowercase + string.ascii_uppercase,
                       string.ascii_lowercase * 2)
deletions = ''.join(ch for ch in map(chr,range(256)) if ch not in letter_set)

s="A235th@#$&( er Ra{}|?>ndom"

def test_original(s):
    tmpStr = s.lower().strip()
    retStrList = []
    for x in tmpStr:
        if x in string.ascii_lowercase:
            retStrList.append(x)

    return ''.join(retStrList)


def test_regex(s):
    return re.sub('[^a-z]', '', s.lower())

def test_regex_closure(s):
  nonascii = re.compile('[^a-z]')
  def replacer(s):
    return nonascii.sub('', s.lower().strip())
  return replacer(s)


def test_str_join(s):
    return ''.join(c for c in s.lower() if c in string.ascii_lowercase)

def test_str_join_set(s):
    return ''.join(c for c in s.lower() if c in letter_set)

def test_filter_set(s):
    return filter(letter_set.__contains__, s.lower())

def test_filter_isalpha(s):
    return filter(str.isalpha, s).lower()

def test_filter_lambda(s):
    return filter(lambda x: x in string.ascii_lowercase, s.lower())

def test_translate(s):
    return string.translate(s, tab, deletions)

for test in sorted(globals()):
    if test.startswith("test_"):
        print "%30s : %s" % (test, timeit.Timer("f(s)", 
              "from __main__ import %s as f, s" % test).timeit(200000))

Acest lucru îmi dă:

       test_filter_isalpha : 1.31981746283
        test_filter_lambda : 2.23935583992
           test_filter_set : 0.76511679557
             test_original : 2.13079176264
                test_regex : 2.44295629752
        test_regex_closure : 2.65205913042
             test_str_join : 2.25571266739
         test_str_join_set : 1.75565888961
            test_translate : 0.269259640541

Se pare că isalpha folosește un algoritm asemănător, cel puțin în ceea ce privește O (), algoritmul set.


Editare: Adăugat setul de filtru, și redenumit funcțiile de filtrare pentru a fi un pic mai clar.

Publicat 12/03/2009 la 15:20
sursa de către utilizator

voturi
6

2.x Python translatemetoda

Conversia în litere mici și filtra non-ascii caractere non-alfa:

from string import ascii_letters, ascii_lowercase, maketrans

table = maketrans(ascii_letters, ascii_lowercase*2)
deletechars = ''.join(set(maketrans('','')) - set(ascii_letters))

print "A235th@#$&( er Ra{}|?>ndom".translate(table, deletechars)
# -> 'atherrandom'

Python 3 translatemetode

Filtru non-ascii:

ascii_bytes = "A235th@#$&(٠٫٢٥ er Ra{}|?>ndom".encode('ascii', 'ignore')

Utilizați bytes.translate()pentru a converti la litere mici și de a șterge bytes non-alfa:

from string import ascii_letters, ascii_lowercase

alpha, lower = [s.encode('ascii') for s in [ascii_letters, ascii_lowercase]]
table = bytes.maketrans(alpha, lower*2)           # convert to lowercase
deletebytes = bytes(set(range(256)) - set(alpha)) # delete nonalpha

print(ascii_bytes.translate(table, deletebytes))
# -> b'atherrandom'
Publicat 12/03/2009 la 16:56
sursa de către utilizator

voturi
0

Python 2.x:

import string
valid_chars= string.ascii_lowercase + string.ascii_uppercase

def only_lower_ascii_alpha(text):
    return filter(valid_chars.__contains__, text).lower()

Funcționează fie cu strsau unicodeargumente.

>>> only_lower_ascii_alpha("Hello there 123456!")
'hellothere'
>>> only_lower_ascii_alpha(u"435 café")
u'caf'
Publicat 22/10/2011 la 17:15
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more