检查字符串的任何(所有)字符是否在给定范围内

时间:2017-12-26 09:00:13

标签: python string python-3.x performance unicode

我有一个包含unicode符号(cyrillic)的字符串:

myString1 = 'Австрия'
myString2 = 'AustriЯ'

我想检查字符串中的所有元素是否都是英文(ASCII)。 现在我使用循环:

for char in myString1:
    if ord(s) not in range(65,91):
         break

因此,如果我找到第一个非英语元素,我就会打破循环。但是对于给定的示例,您可以看到字符串最后可以包含许多英文符号和unicode。通过这种方式,我将检查整个字符串。此外,如果所有的字符串都是英文的,我仍然会检查每个字符。

有没有更有效的方法来做到这一点?我正在考虑这样的事情:

if any(myString[:]) is not in range(65,91)

4 个答案:

答案 0 :(得分:8)

您可以使用setO(1)包含检查)来加快检查速度,尤其是在检查相同范围的多个字符串时,因为初始集创建也需要一次迭代。然后,您可以使用all来获得比any更适合的早期迭代模式:

import string

ascii = set(string.ascii_uppercase)
ascii_all = set(string.ascii_uppercase + string.ascii_lowercase)

if all(x in ascii for x in my_string1):
    # my_string1 is all ascii

当然,任何all构造都可以通过DeMorgan's Law转换为any

if not any(x not in ascii for x in my_string1):
    # my_string1 is all ascii

更新

一种基于纯集的方法,不需要Artyer指出的完整迭代:

if ascii.issuperset(my_string1):
    # my_string1 is all ascii

答案 1 :(得分:2)

另一种方式就像@schwobaseggl建议但使用完整集方法:

import string
ascii = string.ascii_uppercase + string.ascii_lowercase
if set(my_string).issubset(ascii):
    #myString is ascii

答案 2 :(得分:1)

re似乎非常快:

import re

# to check whether any outside ranges (->MatchObject) / all in ranges (->None)
nonletter = re.compile('[^a-zA-Z]').search

# to check whether any in ranges (->MatchObject) / all outside ranges (->None)
letter = re.compile('[a-zA-Z]').search

bool(nonletter(myString1))
# True

bool(nonletter(myString2))
# True

bool(nonletter(myString2[:-1]))
# False

OP的两个例子和一个正例的基准(设置是@schwobaseggl setset是@DanielSanchez):

Австрия
re               0.48832818 ± 0.09022105 µs
set              0.58745548 ± 0.01759877 µs
setset           0.81759223 ± 0.03595184 µs
AustriЯ
re               0.51960442 ± 0.01881561 µs
set              1.03043942 ± 0.02453405 µs
setset           0.54060076 ± 0.01505265 µs
tralala
re               0.27832978 ± 0.01462306 µs
set              0.88285526 ± 0.03792728 µs
setset           0.43238688 ± 0.01847240 µs

基准代码:

import types
from timeit import timeit
import re
import string
import numpy as np

def mnsd(trials):
    return '{:1.8f} \u00b1 {:10.8f} \u00b5s'.format(np.mean(trials), np.std(trials))

nonletter = re.compile('[^a-zA-Z]').search
letterset = set(string.ascii_letters)

def f_re(stri):
    return not nonletter(stri)

def f_set(stri):
    return all(x in letterset for x in stri)

def f_setset(stri):
    return set(stri).issubset(letterset)

for stri in ('Австрия', 'AustriЯ', 'tralala'):
    ref = f_re(stri)
    print(stri)
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        try:
            assert ref == func(stri)
            print("{:16s}".format(name[2:]), mnsd([timeit(
                'f(stri)', globals={'f':func, 'stri':stri}, number=1000) * 1000 for i in range(1000)]))

        except:
            print("{:16s} apparently failed".format(name[2:]))

答案 3 :(得分:0)

没有办法避免迭代。 但是,通过not 65 <= ord(s) <= 91而不是与范围进行比较,您当然可以提高效率。