Tách một chuỗi thành chữ hoa


94

Là gì pythonic cách để tách một chuỗi trước khi xuất hiện của một tập hợp các ký tự?

Ví dụ: tôi muốn tách 'TheLongAndWindingRoad' bất kỳ lần nào xuất hiện một ký tự hoa (có thể ngoại trừ ký tự đầu tiên) và lấy ['The', 'Long', 'And', 'Winding', 'Road'].

Chỉnh sửa: Nó cũng nên phân chia các lần xuất hiện đơn lẻ, tức là từ 'ABC'tôi muốn lấy ['A', 'B', 'C'].

Câu trả lời:


137

Thật không may, không thể tách trên kết quả khớp không có độ rộng bằng 0 trong Python. Nhưng bạn có thể sử dụng re.findallthay thế:

>>> import re
>>> re.findall('[A-Z][^A-Z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']
>>> re.findall('[A-Z][^A-Z]*', 'ABC')
['A', 'B', 'C']

13
Hãy cẩn thận rằng điều này sẽ làm mất bất kỳ ký tự nào trước ký tự viết hoa đầu tiên. 'theLongAndWindingRoad' sẽ cho kết quả [ 'dài', 'Và', 'Winding', 'Đường']
Marc Schulder

14
@MarcSchulder: Nếu bạn cần trường hợp đó, chỉ cần sử dụng '[a-zA-Z][^A-Z]*'làm regex.
knub 10/02/17

Có thể làm tương tự mà không cần viết hoa?
Laurent Cesaro

2
Để phân tách các từ viết hoa lạc đà thấp hơnprint(re.findall('^[a-z]+|[A-Z][^A-Z]*', 'theLongAndWindingRoad'))
hard_working_ant

32

Đây là một giải pháp regex thay thế. Vấn đề có thể được giải quyết lại là "làm cách nào để chèn khoảng trắng trước mỗi ký tự hoa, trước khi thực hiện tách":

>>> s = "TheLongAndWindingRoad ABC A123B45"
>>> re.sub( r"([A-Z])", r" \1", s).split()
['The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']

Điều này có lợi thế là bảo toàn tất cả các ký tự không có khoảng trắng, điều mà hầu hết các giải pháp khác không có.


Bạn có thể giải thích tại sao khoảng trắng trước \ 1 lại hoạt động không? Đó là do phương pháp phân tách hay nó có bất cứ điều gì liên quan đến regex?
Lax_Sam 29/12/18

dấu phân tách tách mặc định cho bất kỳ chuỗi khoảng trắng nào
CIsForCookies

20
>>> import re
>>> re.findall('[A-Z][a-z]*', 'TheLongAndWindingRoad')
['The', 'Long', 'And', 'Winding', 'Road']

>>> re.findall('[A-Z][a-z]*', 'SplitAString')
['Split', 'A', 'String']

>>> re.findall('[A-Z][a-z]*', 'ABC')
['A', 'B', 'C']

Nếu bạn muốn "It'sATest"tách để ["It's", 'A', 'Test']thay đổi rexeg thành"[A-Z][a-z']*"


+1: Đầu tiên để ABC hoạt động. Tôi cũng đã cập nhật câu trả lời của mình bây giờ.
Mark Byers

>>> re.findall ('[AZ] [az] *', "Nó chiếm khoảng 70% nền kinh tế") -----> ['It', 'Economy']
ChristopheD

@ChristopheD. OP không nói cách xử lý các ký tự không phải alpha.
John La Rooy

1
true, nhưng cách regex hiện tại này cũng dành cho dropstất cả các từ thông thường (chỉ là alpha đơn thuần) không bắt đầu bằng chữ hoa. Tôi nghi ngờ rằng đó là ý định của OP.
ChristopheD

8

Một biến thể về giải pháp của @ChristopheD

s = 'TheLongAndWindingRoad'

pos = [i for i,e in enumerate(s+'A') if e.isupper()]
parts = [s[pos[j]:pos[j+1]] for j in xrange(len(pos)-1)]

print parts

2
Tuyệt vời - điều này cũng hoạt động với các ký tự không phải Latinh. Các giải pháp regex được hiển thị ở đây không.
AlexVhr

7

Sử dụng một cái nhìn trước:

Trong Python 3.7, bạn có thể làm điều này:

re.split('(?=[A-Z])', 'theLongAndWindingRoad')

Và nó mang lại:

['the', 'Long', 'And', 'Winding', 'Road']

5
import re
filter(None, re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad"))

hoặc là

[s for s in re.split("([A-Z][^A-Z]*)", "TheLongAndWindingRoad") if s]

1
Bộ lọc này là hoàn toàn không cần thiết và mua bạn không có gì hơn một chia regex trực tiếp với nhóm chụp: [s for s in re.compile(r"([A-Z][^A-Z]*)").split( "TheLongAndWindingRoad") if s]cho['The', 'Long', 'And', 'Winding', 'Road']
SMCI

1
@smci: Cách sử dụng filternày giống như cách hiểu danh sách với một điều kiện. Bạn có bất cứ điều gì chống lại nó?
Gabe

1
Tôi biết nó có thể được thay thế bằng một danh sách hiểu với một điều kiện, bởi vì tôi vừa đăng mã đó, sau đó bạn đã sao chép nó. Dưới đây là ba lý do khiến khả năng hiểu danh sách được ưa thích hơn: a) Thành ngữ dễ hiểu : tính hiểu danh sách là một thành ngữ Pythonic nhiều hơn và đọc rõ ràng từ trái sang phải hơn filter(lambdaconditionfunc, ...)b) trong Python 3, filter()trả về một trình lặp. Vì vậy, chúng sẽ không hoàn toàn tương đương. c) Tôi cũng mong đợi filter()là chậm hơn
smci

4
src = 'TheLongAndWindingRoad'
glue = ' '

result = ''.join(glue + x if x.isupper() else x for x in src).strip(glue).split(glue)

1
Bạn có thể vui lòng giải thích thêm tại sao đây là giải pháp tốt cho vấn đề.
Matas Vaitkevicius,

Tôi xin lỗi. Tôi quên bước cuối cùng
user3726655

Với tôi, có vẻ ngắn gọn, dễ hiểu và dễ hiểu.

4

Tôi nghĩ rằng câu trả lời tốt hơn có thể là chia chuỗi thành các từ không kết thúc bằng chữ hoa. Điều này sẽ xử lý trường hợp chuỗi không bắt đầu bằng chữ hoa.

 re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoad')

thí dụ:

>>> import re
>>> re.findall('.[^A-Z]*', 'aboutTheLongAndWindingRoadABC')
['about', 'The', 'Long', 'And', 'Winding', 'Road', 'A', 'B', 'C']

2

Giải pháp thay thế (nếu bạn không thích regex rõ ràng):

s = 'TheLongAndWindingRoad'

pos = [i for i,e in enumerate(s) if e.isupper()]

parts = []
for j in xrange(len(pos)):
    try:
        parts.append(s[pos[j]:pos[j+1]])
    except IndexError:
        parts.append(s[pos[j]:])

print parts

1

Một cái khác không có regex và khả năng giữ chữ hoa liền nhau nếu muốn

def split_on_uppercase(s, keep_contiguous=False):
    """

    Args:
        s (str): string
        keep_contiguous (bool): flag to indicate we want to 
                                keep contiguous uppercase chars together

    Returns:

    """

    string_length = len(s)
    is_lower_around = (lambda: s[i-1].islower() or 
                       string_length > (i + 1) and s[i + 1].islower())

    start = 0
    parts = []
    for i in range(1, string_length):
        if s[i].isupper() and (not keep_contiguous or is_lower_around()):
            parts.append(s[start: i])
            start = i
    parts.append(s[start:])

    return parts

>>> split_on_uppercase('theLongWindingRoad')
['the', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWindingRoad')
['The', 'Long', 'Winding', 'Road']
>>> split_on_uppercase('TheLongWINDINGRoadT', True)
['The', 'Long', 'WINDING', 'Road', 'T']
>>> split_on_uppercase('ABC')
['A', 'B', 'C']
>>> split_on_uppercase('ABCD', True)
['ABCD']
>>> split_on_uppercase('')
['']
>>> split_on_uppercase('hello world')
['hello world']

1

Điều này có thể thực hiện được với more_itertools.split_beforecông cụ này.

import more_itertools as mit


iterable = "TheLongAndWindingRoad"
[ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())]
# ['The', 'Long', 'And', 'Winding', 'Road']

Nó cũng nên tách các lần xuất hiện đơn lẻ, tức là từ 'ABC'tôi muốn lấy ['A', 'B', 'C'].

iterable = "ABC"
[ "".join(i) for i in mit.split_before(iterable, pred=lambda s: s.isupper())]
# ['A', 'B', 'C']

more_itertoolslà một gói của bên thứ ba với hơn 60 công cụ hữu ích bao gồm các cách triển khai cho tất cả các công thức itertools ban đầu, ngăn cản việc triển khai thủ công của họ.


0

Một cách khác mà không cần sử dụng regex hoặc enumerate:

word = 'TheLongAndWindingRoad'
list = [x for x in word]

for char in list:
    if char != list[0] and char.isupper():
        list[list.index(char)] = ' ' + char

fin_list = ''.join(list).split(' ')

Tôi nghĩ rằng nó rõ ràng và đơn giản hơn mà không cần xâu chuỗi quá nhiều phương pháp hoặc sử dụng một danh sách dài có thể khó đọc.


0

Một cách thay thế sử dụng enumerateisupper()

Mã:

strs = 'TheLongAndWindingRoad'
ind =0
count =0
new_lst=[]
for index, val in enumerate(strs[1:],1):
    if val.isupper():
        new_lst.append(strs[ind:index])
        ind=index
if ind<len(strs):
    new_lst.append(strs[ind:])
print new_lst

Đầu ra:

['The', 'Long', 'And', 'Winding', 'Road']

0

Chia sẻ những gì tôi nghĩ đến khi tôi đọc bài đăng. Khác với các bài viết khác.

strs = 'TheLongAndWindingRoad'

# grab index of uppercase letters in strs
start_idx = [i for i,j in enumerate(strs) if j.isupper()]

# create empty list
strs_list = []

# initiate counter
cnt = 1

for pos in start_idx:
    start_pos = pos

    # use counter to grab next positional element and overlook IndexeError
    try:
        end_pos = start_idx[cnt]
    except IndexError:
        continue

    # append to empty list
    strs_list.append(strs[start_pos:end_pos])

    cnt += 1

0

Cách Pythonic có thể là:

"".join([(" "+i if i.isupper() else i) for i in 'TheLongAndWindingRoad']).strip().split()
['The', 'Long', 'And', 'Winding', 'Road']

Hoạt động tốt cho Unicode, tránh re / re2.

"".join([(" "+i if i.isupper() else i) for i in 'СуперМаркетыПродажаКлиент']).strip().split()
['Супер', 'Маркеты', 'Продажа', 'Клиент']

-1

Thay thế mọi chữ cái viết hoa 'L' trong phần đã cho bằng một khoảng trống cộng với chữ cái đó là "L". Chúng ta có thể thực hiện việc này bằng cách sử dụng tính năng hiểu danh sách hoặc chúng ta có thể xác định một hàm để làm điều đó như sau.

s = 'TheLongANDWindingRoad ABC A123B45'
''.join([char if (char.islower() or not char.isalpha()) else ' '+char for char in list(s)]).strip().split()
>>> ['The', 'Long', 'A', 'N', 'D', 'Winding', 'Road', 'A', 'B', 'C', 'A123', 'B45']

Nếu bạn chọn đi theo một chức năng, đây là cách thực hiện.

def splitAtUpperCase(text):
    result = ""
    for char in text:
        if char.isupper():
            result += " " + char
        else:
            result += char
    return result.split()

Trong trường hợp của ví dụ đã cho:

print(splitAtUpperCase('TheLongAndWindingRoad')) 
>>>['The', 'Long', 'A', 'N', 'D', 'Winding', 'Road']

Nhưng hầu hết thời gian chúng ta tách một câu thành các chữ cái viết hoa, thường là trường hợp chúng ta muốn duy trì các chữ viết tắt thường là một dòng chữ hoa liên tục. Đoạn mã dưới đây sẽ hữu ích.

def splitAtUpperCase(s):
    for i in range(len(s)-1)[::-1]:
        if s[i].isupper() and s[i+1].islower():
            s = s[:i]+' '+s[i:]
        if s[i].isupper() and s[i-1].islower():
            s = s[:i]+' '+s[i:]
    return s.split()

splitAtUpperCase('TheLongANDWindingRoad')

>>> ['The', 'Long', 'AND', 'Winding', 'Road']

Cảm ơn.


@MarkByers Tôi không biết tại sao ai đó lại bỏ phiếu cho câu trả lời của tôi nhưng tôi rất muốn bạn xem nó cho tôi. Tôi muốn đánh giá phản hồi của bạn.
Samuel Nde
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.