it-swarm-vi.tech

Bỏ qua trường hợp trong chuỗi Python

Cách dễ nhất để so sánh các chuỗi trong Python, bỏ qua trường hợp là gì?

Tất nhiên người ta có thể làm (str1.lower () <= str2.lower ()), v.v., nhưng điều này tạo ra hai chuỗi tạm thời bổ sung (với các chi phí phân bổ/g-c rõ ràng).

Tôi đoán tôi đang tìm kiếm một tương đương với stricmp của C ().

[Một số bối cảnh được yêu cầu, vì vậy tôi sẽ chứng minh bằng một ví dụ tầm thường:]

Giả sử bạn muốn sắp xếp một danh sách các chuỗi looong. Bạn chỉ cần thực hiện so sánh chuỗiList.sort () . Đây là so sánh chuỗi O (n * log (n)) và không quản lý bộ nhớ (vì tất cả Chuỗi và các thành phần danh sách là một số loại con trỏ thông minh). Bạn đang hạnh phúc.

Bây giờ, bạn muốn làm như vậy, nhưng bỏ qua trường hợp (hãy đơn giản hóa và nói Tất cả các chuỗi là ascii, vì vậy các vấn đề cục bộ có thể được bỏ qua) . Bạn có thể thực hiện ListList.sort (key = lambda s: s .lower ()), nhưng sau đó bạn gây ra hai phân bổ .__ mới cho mỗi so sánh, cộng với gánh nặng cho trình thu gom rác với các chuỗi (đã hạ xuống) trùng lặp. Mỗi tiếng ồn quản lý bộ nhớ như vậy là chậm hơn so với chuỗi so sánh đơn giản.

Bây giờ, với hàm stricmp () giống như tại chỗ, bạn thực hiện: theList.sort (cmp = stricmp) Và nó nhanh và thân thiện với bộ nhớ như theList.sort (). Bạn lại vui vẻ.

Vấn đề là bất kỳ so sánh không phân biệt chữ hoa chữ thường dựa trên Python đều liên quan đến chuỗi ẩn Sao chép, vì vậy tôi đã mong đợi tìm thấy một so sánh dựa trên C (có thể trong chuỗi mô-đun).

Không thể tìm thấy bất cứ điều gì như vậy, do đó câu hỏi ở đây . (Hy vọng điều này làm rõ câu hỏi).

51
Paul Oyster

Dưới đây là điểm chuẩn cho thấy rằng sử dụng str.lower nhanh hơn phương thức đề xuất của câu trả lời được chấp nhận (libc.strcasecmp):

#!/usr/bin/env python2.7
import random
import timeit

from ctypes import *
libc = CDLL('libc.dylib') # change to 'libc.so.6' on linux

with open('/usr/share/dict/words', 'r') as wordlist:
    words = wordlist.read().splitlines()
random.shuffle(words)
print '%i words in list' % len(words)

setup = 'from __main__ import words, libc; gc.enable()'
stmts = [
    ('simple sort', 'sorted(words)'),
    ('sort with key=str.lower', 'sorted(words, key=str.lower)'),
    ('sort with cmp=libc.strcasecmp', 'sorted(words, cmp=libc.strcasecmp)'),
]

for (comment, stmt) in stmts:
    t = timeit.Timer(stmt=stmt, setup=setup)
    print '%s: %.2f msec/pass' % (comment, (1000*t.timeit(10)/10))

thời gian điển hình trên máy của tôi:

235886 words in list
simple sort: 483.59 msec/pass
sort with key=str.lower: 1064.70 msec/pass
sort with cmp=libc.strcasecmp: 5487.86 msec/pass

Vì vậy, phiên bản với str.lower không chỉ nhanh nhất mà còn là ứng dụng di động và Pythonic nhất trong tất cả các giải pháp được đề xuất ở đây . Tôi chưa mô tả cách sử dụng bộ nhớ, nhưng poster ban đầu vẫn chưa đưa ra lý do thuyết phục lo lắng về nó. Ngoài ra, ai nói rằng một cuộc gọi vào mô-đun libc không trùng lặp bất kỳ chuỗi nào?

Lưu ý: Phương thức chuỗi lower() cũng có ưu điểm là phụ thuộc vào miền địa phương. Một cái gì đó bạn có thể sẽ không nhận được đúng khi viết giải pháp "tối ưu hóa" của riêng bạn. Mặc dù vậy, do lỗi và các tính năng bị thiếu trong Python, loại so sánh này có thể cho bạn kết quả sai trong bối cảnh unicode.

74
user3850

Bạn có đang sử dụng so sánh này trong một đường dẫn được thực hiện rất thường xuyên của một ứng dụng có độ nhạy hiệu năng cao không? Ngoài ra, bạn có đang chạy cái này trên các chuỗi có kích thước megabyte không? Nếu không, thì bạn không nên lo lắng về hiệu suất và chỉ sử dụng phương thức .lower ().

Đoạn mã sau chứng minh rằng thực hiện so sánh không phân biệt chữ hoa chữ thường bằng cách gọi .lower () trên hai chuỗi có kích thước gần bằng một megabyte mất khoảng 0,009 giây trên máy tính để bàn 1,8 GHz của tôi:

from timeit import Timer

s1 = "1234567890" * 100000 + "a"
s2 = "1234567890" * 100000 + "B"

code = "s1.lower() < s2.lower()"
time = Timer(code, "from __main__ import s1, s2").timeit(1000)
print time / 1000   # 0.00920499992371 on my machine

Nếu thực sự đây là một phần mã cực kỳ quan trọng, hiệu năng, thì tôi khuyên bạn nên viết một hàm trong C và gọi nó từ mã Python của bạn, vì điều đó sẽ cho phép bạn thực hiện tìm kiếm không phân biệt chữ hoa chữ thường. Chi tiết về cách viết các mô-đun mở rộng C có thể được tìm thấy ở đây: https://docs.python.org/extending/extending.html

7
Eli Courtwright

Câu hỏi của bạn ngụ ý rằng bạn không cần Unicode. Hãy thử đoạn mã sau đây; nếu nó hiệu quả với bạn, bạn đã hoàn thành:

Python 2.5.2 (r252:60911, Aug 22 2008, 02:34:17)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, "en_US")
'en_US'
>>> sorted("ABCabc", key=locale.strxfrm)
['a', 'A', 'b', 'B', 'c', 'C']
>>> sorted("ABCabc", cmp=locale.strcoll)
['a', 'A', 'b', 'B', 'c', 'C']

Làm rõ: trong trường hợp không rõ ràng ngay từ cái nhìn đầu tiên, locale.strcoll dường như là hàm bạn cần, tránh các chuỗi "trùng lặp" str.lower hoặc locale.strxfrm.

7
tzot

Tôi không thể tìm thấy bất kỳ cách tích hợp nào khác để thực hiện so sánh không phân biệt chữ hoa chữ thường: Công thức sách nấu ăn python sử dụng thấp hơn ().

Tuy nhiên, bạn phải cẩn thận khi sử dụng thấp hơn để so sánh vì vấn đề Thổ Nhĩ Kỳ I . Thật không may, xử lý của Python cho tiếng Thổ Nhĩ Kỳ là không tốt. Tôi được chuyển đổi thành I, nhưng tôi không được chuyển đổi thành. Tôi được chuyển đổi thành i, nhưng tôi không được chuyển đổi thành. 

5
Douglas Leeder

Không có chức năng nào tương đương với chức năng mà bạn muốn.

Bạn có thể viết hàm riêng của mình chuyển đổi thành .lower () mỗi ký tự để tránh trùng lặp cả hai chuỗi, nhưng tôi chắc chắn rằng nó sẽ rất chuyên sâu và cực kỳ kém hiệu quả. 

Trừ khi bạn đang làm việc với các chuỗi cực dài (quá dài có thể gây ra vấn đề về bộ nhớ nếu bị trùng lặp) thì tôi sẽ giữ cho nó đơn giản và sử dụng 

str1.lower() == str2.lower()

Bạn sẽ ổn thôi

3
Ricardo Reyes

Câu hỏi này đang hỏi 2 điều rất khác nhau:

  1. Cách dễ nhất để so sánh các chuỗi trong Python, bỏ qua trường hợp là gì?
  2. Tôi đoán tôi đang tìm kiếm một tương đương với C 'stricmp ().

Vì # 1 đã được trả lời rất tốt rồi (ví dụ: str1.lower () <str2.lower ()) nên tôi sẽ trả lời # 2.

def strincmp(str1, str2, numchars=None):
    result = 0
    len1 = len(str1)
    len2 = len(str2)
    if numchars is not None:
        minlen = min(len1,len2,numchars)
    else:
        minlen = min(len1,len2)
    #end if
    orda = ord('a')
    ordz = ord('z')

    i = 0
    while i < minlen and 0 == result:
        ord1 = ord(str1[i])
        ord2 = ord(str2[i])
        if ord1 >= orda and ord1 <= ordz:
            ord1 = ord1-32
        #end if
        if ord2 >= orda and ord2 <= ordz:
            ord2 = ord2-32
        #end if
        result = cmp(ord1, ord2)
        i += 1
    #end while

    if 0 == result and minlen != numchars:
        if len1 < len2:
            result = -1
        Elif len2 < len1:
            result = 1
        #end if
    #end if

    return result
#end def

Chỉ sử dụng chức năng này khi có ý nghĩa như trong nhiều trường hợp, kỹ thuật viết thường sẽ vượt trội hơn.

Tôi chỉ làm việc với các chuỗi ascii, tôi không chắc điều này sẽ hoạt động như thế nào với unicode.

2
trevorcroft

Khi một cái gì đó không được hỗ trợ tốt trong thư viện tiêu chuẩn, tôi luôn tìm kiếm gói PyPI. Với ảo hóa và sự phổ biến của các bản phân phối Linux hiện đại, tôi không còn tránh các tiện ích mở rộng Python. PyICU dường như phù hợp với hóa đơn: https://stackoverflow.com/a/1098160/3461

Bây giờ cũng có một tùy chọn đó là trăn thuần túy. Nó đã được thử nghiệm tốt: https://github.com/jtauber/pyuca


Câu trả lời cũ:

Tôi thích các giải pháp biểu thức thông thường. Đây là một chức năng bạn có thể sao chép và dán vào bất kỳ chức năng nào, nhờ hỗ trợ cấu trúc khối của python.

def equals_ignore_case(str1, str2):
    import re
    return re.match(re.escape(str1) + r'\Z', str2, re.I) is not None

Vì tôi đã sử dụng kết hợp thay vì tìm kiếm, tôi không cần thêm dấu mũ (^) vào biểu thức thông thường.

Lưu ý: Điều này chỉ kiểm tra sự bình đẳng, đôi khi là những gì cần thiết. Tôi cũng sẽ không đi xa để nói rằng tôi thích nó.

2
Benjamin Atkin

Thành ngữ được đề xuất để sắp xếp danh sách các giá trị bằng cách sử dụng các khóa đắt tiền để tính toán là "mẫu trang trí". Nó chỉ đơn giản là trong việc xây dựng một danh sách các khóa (khóa, giá trị) từ danh sách ban đầu và sắp xếp danh sách đó. Sau đó, việc loại bỏ các khóa và lấy danh sách các giá trị được sắp xếp là chuyện nhỏ:

>>> original_list = ['a', 'b', 'A', 'B']
>>> decorated = [(s.lower(), s) for s in original_list]
>>> decorated.sort()
>>> sorted_list = [s[1] for s in decorated]
>>> sorted_list
['A', 'a', 'B', 'b']

Hoặc nếu bạn thích một lớp lót:

>>> sorted_list = [s[1] for s in sorted((s.lower(), s) for s in original_list)]
>>> sorted_list
['A', 'a', 'B', 'b']

Nếu bạn thực sự lo lắng về chi phí gọi thấp hơn (), bạn có thể lưu trữ bộ dữ liệu (chuỗi thấp hơn, chuỗi gốc) ở mọi nơi. Tuples là loại container rẻ nhất trong Python, chúng cũng có thể băm để chúng có thể được sử dụng làm khóa từ điển, thiết lập thành viên, v.v.

1
Antoine P.

Đây là cách bạn sẽ làm với re:

import re
p = re.compile('^hello$', re.I)
p.match('Hello')
p.match('hello')
p.match('HELLO')
1
Moses Ting

Đối với các so sánh không thường xuyên hoặc thậm chí lặp đi lặp lại, một vài đối tượng chuỗi bổ sung không nên quan trọng miễn là điều này sẽ không xảy ra trong vòng lặp trong cùng của mã lõi của bạn hoặc bạn không có đủ dữ liệu để thực sự nhận thấy tác động hiệu suất. Xem nếu bạn làm: làm mọi thứ theo cách "ngu ngốc" sẽ ít ngu ngốc hơn nếu bạn cũng làm ít hơn.

Nếu bạn thực sự muốn tiếp tục so sánh nhiều và rất nhiều trường hợp văn bản không nhạy cảm, bạn có thể giữ các phiên bản chữ thường của chuỗi để tránh hoàn thiện và tạo lại hoặc bình thường hóa toàn bộ dữ liệu thành chữ thường. Điều này tất nhiên phụ thuộc vào kích thước của tập dữ liệu. Nếu có một số kim tương đối và một đống cỏ khô lớn, việc thay thế kim bằng các đối tượng regrec đã biên dịch là một giải pháp. Nếu khó nói mà không thấy một ví dụ cụ thể.

0
yason

Bạn có thể dịch từng chuỗi thành chữ thường một lần --- chỉ một cách uể oải khi bạn cần nó, hoặc như một sự chuẩn bị cho loại nếu bạn biết bạn sẽ sắp xếp toàn bộ bộ sưu tập chuỗi. Có một số cách để gắn khóa so sánh này với dữ liệu thực tế được sắp xếp, nhưng những kỹ thuật này cần được giải quyết trong một vấn đề riêng.

Lưu ý rằng kỹ thuật này có thể được sử dụng không chỉ để xử lý các vấn đề chữ hoa/chữ thường, mà đối với các loại sắp xếp khác như sắp xếp cụ thể theo địa phương hoặc sắp xếp tiêu đề "Kiểu thư viện" mà bỏ qua các bài viết hàng đầu và bình thường hóa dữ liệu trước khi sắp xếp nó.

0
Dale Wilson

Chỉ cần sử dụng phương thức str().lower(), trừ khi hiệu suất cao là quan trọng - trong trường hợp đó hãy viết phương thức sắp xếp đó dưới dạng phần mở rộng C.

"Cách viết tiện ích mở rộng Python" có vẻ như là một phần giới thiệu đàng hoàng ..

Thú vị hơn, Hướng dẫn này so sánh bằng cách sử dụng thư viện ctypes so với viết mô-đun C bên ngoài (ctype khá chậm so với phần mở rộng C).

0
dbr
import re
if re.match('tEXT', 'text', re.IGNORECASE):
    # is True
0
Venkatesh Bachu

Tôi khá chắc chắn rằng bạn phải sử dụng .lower () hoặc sử dụng biểu thức chính quy. Tôi không biết chức năng so sánh chuỗi không phân biệt chữ hoa chữ thường.

0
Mark Biek