簡體   English   中英

Python中將字符串轉換為全部小寫的最有效方法是刪除所有非ascii字母字符?

[英]What is the most efficient way in Python to convert a string to all lowercase stripping out all non-ascii alpha characters?

我需要在Python中執行一個簡單的任務,即將字符串轉換為全部小寫並刪除所有非ascii非字母字符。

例如:

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

我有一個簡單的功能來做到這一點:

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)

但我不禁想到有更高效,更優雅的方式。

謝謝!


編輯:

感謝所有回答的人。 我學會了,並且在某些情況下重新學習了很多python。

另一個解決方案(不是pythonic,但非常快)是使用string.translate - 但請注意,這不適用於unicode。 值得注意的是,您可以通過將字符移動到一個集合(通過哈希查找,而不是每次執行線性搜索)來加速Dana的代碼 以下是我給出的各種解決方案的時間安排:

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

這給了我:

               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

[編輯]也更新了過濾器解決方案。 (注意,使用set.__contains__在這里有很大的不同,因為它避免為lambda進行額外的函數調用。

>>> filter(str.isalpha, "This is a Test").lower()
'thisisatest'
>>> filter(str.isalpha, "A235th@#$&( er Ra{}|?>ndom").lower()
'atherrandom'

不是特別的運行時效率,但在糟糕,疲憊的編碼器眼睛肯定更好:

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

我會:

  • 小寫字符串
  • ""替換所有[^az]

像那樣:

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

編輯:事實證明原始版本(下面)非常慢,但通過將其轉換為閉包(上圖)可以獲得一些性能。

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

我的性能測量結果是針對字符串的100,000次迭代

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

透露:

  • f_re_0 took 2672.000 ms (這是這個答案的原始版本)
  • f_re_1 took 2109.000 ms (這是上面顯示的閉包版本)
  • f_re_2 took 2031.000 ms (關閉版本,沒有冗余strip()
  • f_fl_1 took 1953.000 ms展開filter / lambda版本
  • f_fl_2 took 1485.000 msCoady的filter版本
  • f_jn_1 took 1860.000 msDana的join

為了測試,我沒有print結果。

Python 2.x translate方法

轉換為小寫並過濾非ascii非字母字符:

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 translate方法

過濾非ascii:

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

使用bytes.translate()轉換為小寫並刪除非alpha字節:

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'

與@ Dana類似,但我認為這聽起來像是一個過濾工作,應該在代碼中可見。 也無需顯式調用join()

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

我在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))

這給了我:

       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

似乎isalpha正在使用類似的算法,至少在O()方面,對於集合算法。


編輯:添加了過濾器集,並將過濾器功能重命名為更加清晰。

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

做與你基本相同的事情。

這是列表擴展的典型應用:

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

它不會過濾掉“<”(html實體),就像在你的例子中一樣,但我認為這是意外刪除和過去的問題。

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

使用strunicode參數。

>>> only_lower_ascii_alpha("Hello there 123456!")
'hellothere'
>>> only_lower_ascii_alpha(u"435 café")
u'caf'

我個人會使用正則表達式,然后將最終的字符串轉換為小寫。 我不知道如何用Python編寫它,但基本的想法是:

  1. 刪除字符串中與字符串不敏感的正則表達式“ \\w ”不匹配的字符
  2. 將字符串轉換為小寫

或相反亦然。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM