เมื่อแยกสตริงว่างใน Python ทำไม split () ส่งคืนรายการว่างขณะที่ split ('\ n') ส่งคืน ['']


155

ฉันใช้split('\n')จะได้รับสายในสายหนึ่งและพบว่า''.split()ผลตอบแทนที่รายการที่ว่างเปล่า, []ในขณะที่ผลตอบแทน''.split('\n') ['']มีเหตุผลเฉพาะสำหรับความแตกต่างดังกล่าวหรือไม่?

และมีวิธีที่สะดวกกว่าในการนับจำนวนบรรทัดในสตริงหรือไม่?


คำตอบ:


247

คำถาม: ฉันใช้ split ('\ n') เพื่อรับบรรทัดในหนึ่งสตริงและพบว่า '' .split () ส่งคืนรายการว่าง [] ขณะที่ '' .split ('\ n') ส่งคืน [''] .

วิธีการstr.split ()มีสองขั้นตอนวิธี หากไม่มีการกำหนดอาร์กิวเมนต์มันจะทำการแยก whitespace ซ้ำ ๆ อย่างไรก็ตามหากมีการกำหนดอาร์กิวเมนต์มันจะถือว่าเป็นตัวคั่นเดียวที่ไม่มีการรันซ้ำ

ในกรณีของการแยกสตริงว่างโหมดแรก (ไม่มีอาร์กิวเมนต์) จะส่งคืนรายการว่างเนื่องจากช่องว่างถูกกินและไม่มีค่าที่จะใส่ในรายการผลลัพธ์

ในทางตรงกันข้ามโหมดที่สอง (พร้อมอาร์กิวเมนต์เช่น\n) จะสร้างฟิลด์ว่างอันแรก พิจารณาถ้าคุณเขียน'\n'.split('\n')คุณจะได้รับสองช่อง (หนึ่งแยกให้สองครึ่ง)

คำถาม: มีเหตุผลที่เฉพาะเจาะจงสำหรับความแตกต่างดังกล่าวหรือไม่?

โหมดแรกนี้มีประโยชน์เมื่อมีการจัดแนวข้อมูลในคอลัมน์ที่มีช่องว่างจำนวนตัวแปร ตัวอย่างเช่น:

>>> data = '''\
Shasta      California     14,200
McKinley    Alaska         20,300
Fuji        Japan          12,400
'''
>>> for line in data.splitlines():
        print line.split()

['Shasta', 'California', '14,200']
['McKinley', 'Alaska', '20,300']
['Fuji', 'Japan', '12,400']

โหมดที่สองมีประโยชน์สำหรับข้อมูลที่มีการคั่นเช่น CSV ซึ่งเครื่องหมายจุลภาคที่ซ้ำกันแสดงถึงช่องว่าง ตัวอย่างเช่น:

>>> data = '''\
Guido,BDFL,,Amsterdam
Barry,FLUFL,,USA
Tim,,,USA
'''
>>> for line in data.splitlines():
        print line.split(',')

['Guido', 'BDFL', '', 'Amsterdam']
['Barry', 'FLUFL', '', 'USA']
['Tim', '', '', 'USA']

หมายเหตุจำนวนฟิลด์ผลลัพธ์หนึ่งฟิลด์มากกว่าจำนวนตัวคั่น คิดว่าการตัดเชือก หากคุณไม่ตัดคุณมีชิ้นเดียว ทำให้หนึ่งตัดให้สองชิ้น ทำให้สองแผลให้สามชิ้น และด้วยวิธีการของ Python str.split (ตัวคั่น) :

>>> ''.split(',')       # No cuts
['']
>>> ','.split(',')      # One cut
['', '']
>>> ',,'.split(',')     # Two cuts
['', '', '']

คำถาม: และมีวิธีที่สะดวกกว่าในการนับจำนวนบรรทัดในสตริงหรือไม่?

ใช่มีสองวิธีง่าย ๆ หนึ่งใช้str.count ()และการใช้งานอื่น ๆstr.splitlines () \nทั้งสองวิธีจะให้คำตอบเดียวกันเว้นแต่บรรทัดสุดท้ายเป็นที่ขาดหายไป หากบรรทัดใหม่สุดท้ายหายไปแนวทางstr.splitlinesจะให้คำตอบที่ถูกต้อง เทคนิคที่เร็วกว่าและแม่นยำนั้นใช้วิธีนับ แต่แก้ไขให้ถูกต้องสำหรับ newline สุดท้าย:

>>> data = '''\
Line 1
Line 2
Line 3
Line 4'''

>>> data.count('\n')                               # Inaccurate
3
>>> len(data.splitlines())                         # Accurate, but slow
4
>>> data.count('\n') + (not data.endswith('\n'))   # Accurate and fast
4    

คำถามจาก @Kaz: ทำไมห่าถึงเป็นอัลกอริธึมที่แตกต่างกันสองอย่างที่เขาใส่เข้าไปในฟังก์ชันเดียว?

ลายเซ็นสำหรับstr.split มีอายุประมาณ 20 ปีและ API จำนวนหนึ่งในยุคนั้นมีความจริงจัง ถึงแม้จะไม่สมบูรณ์แบบ แต่วิธีการก็ไม่ได้ "น่ากลัว" เช่นกัน ส่วนใหญ่ตัวเลือกการออกแบบ API ของ Guido นั้นยืนหยัดได้ด้วยการทดสอบกาลเวลา

API ปัจจุบันไม่มีข้อได้เปรียบ พิจารณาสตริงเช่น:

ps_aux_header  = "USER               PID  %CPU %MEM      VSZ"
patient_header = "name,age,height,weight"

เมื่อถูกขอให้แบ่งสตริงเหล่านี้ลงในช่องคนมักจะอธิบายทั้งสองโดยใช้คำภาษาอังกฤษ "แบ่ง" เมื่อถูกขอให้อ่านโค้ดเช่นfields = line.split() หรือfields = line.split(',')คนมักจะตีความคำสั่งอย่างถูกต้องว่า "แยกบรรทัดออกเป็นฟิลด์"

เครื่องมือText-to-Columnของ Microsoft Excel สร้างตัวเลือก API ที่คล้ายกันและรวมอัลกอริทึมการแยกทั้งสองในเครื่องมือเดียวกัน ผู้คนดูเหมือนจะทำตัวเป็นแบบอย่างของการแยกฟิลด์เป็นแนวคิดเดียวแม้ว่าจะมีอัลกอริทึมมากกว่าหนึ่งเกี่ยวข้องก็ตาม


28

ดูเหมือนว่าเป็นวิธีที่ควรจะทำงานตามเอกสาร :

['']แยกสตริงที่ว่างเปล่ากับที่ระบุผลตอบแทนที่คั่น

หากไม่ได้ระบุ sep หรือเป็น None จะใช้อัลกอริทึมการแยกที่แตกต่างกัน: การรันของช่องว่างต่อเนื่องจะถือเป็นตัวคั่นเดียวและผลลัพธ์จะไม่มีสตริงว่างที่จุดเริ่มต้นหรือสิ้นสุดหากสตริงมีช่องว่างนำหน้าหรือต่อท้าย ดังนั้นการแยกสตริงว่างหรือสตริงที่ประกอบด้วยเพียงช่องว่างที่มีตัวคั่นไม่มีผลตอบแทน []

ดังนั้นเพื่อให้ชัดเจนsplit()ฟังก์ชั่นจึงใช้อัลกอริทึมการแยกสองแบบที่แตกต่างกันและใช้การมีอยู่ของอาร์กิวเมนต์เพื่อตัดสินใจว่าจะเรียกใช้อันใด นี่อาจเป็นเพราะมันช่วยให้การเพิ่มประสิทธิภาพหนึ่งสำหรับไม่มีข้อโต้แย้งมากกว่าหนึ่งที่มีข้อโต้แย้ง; ฉันไม่รู้


4

.split()ไม่มีพารามิเตอร์พยายามที่จะฉลาด มันแยกในช่องว่างแท็บช่องว่างสายฟีดและอื่น ๆ และมันก็ข้ามสตริงว่างทั้งหมดเป็นผลมาจากสิ่งนี้

>>> "  fii    fbar \n bopp ".split()
['fii', 'fbar', 'bopp']

เป็นหลัก.split()โดยไม่ต้องใช้พารามิเตอร์ที่ใช้ในการแยกคำจากสตริงเมื่อเทียบ.split()กับพารามิเตอร์ที่เพิ่งใช้สตริงและแยกมัน

นั่นคือเหตุผลของความแตกต่าง

และใช่การนับเส้นด้วยการแยกไม่ใช่วิธีที่มีประสิทธิภาพ นับจำนวนบรรทัดฟีดและเพิ่มหนึ่งรายการถ้าสตริงไม่ได้ลงท้ายด้วยตัวดึงข้อมูลบรรทัด


2

การใช้count():

s = "Line 1\nLine2\nLine3"
n_lines = s.count('\n') + 1

4
การ +1 ควรทำได้ก็ต่อเมื่อข้อความไม่ได้ลงท้ายด้วย '\ n'
Lennart Regebro

8
ถ้ามันลงท้ายด้วย "\ n" แล้วบรรทัดสุดท้ายคือบรรทัดว่าง แม้ว่าจะไร้ประโยชน์ แต่ก็ยังนับเป็นบรรทัดใช่ไหม?
Jakub M.

2
ไม่ เมื่อฉันเขียนข้อความ 3 บรรทัดลงในไฟล์และจบแต่ละบรรทัดด้วย linefeed ฉันจะบอกว่าไฟล์นั้นมี 3 บรรทัด ในยูนิกซ์เป็นวิธีที่ดีที่สุดที่จะมีไฟล์ข้อความลงท้ายด้วย linefeed เสมอ มิฉะนั้นจะcat fileทำให้บรรทัดคำสั่งของคุณและการโค่นล้มบ่น vi จะผนวกหนึ่งเสมอ
user829755

2
>>> print str.split.__doc__
S.split([sep [,maxsplit]]) -> list of strings

Return a list of the words in the string S, using sep as the
delimiter string.  If maxsplit is given, at most maxsplit
splits are done. If sep is not specified or is None, any
whitespace string is a separator and empty strings are removed
from the result.

สังเกตประโยคสุดท้าย

ในการนับจำนวนบรรทัดคุณสามารถนับได้ว่า\nมีจำนวนเท่าใด:

line_count = some_string.count('\n') + some_string[-1] != '\n'

ส่วนสุดท้ายคำนึงถึงบรรทัดสุดท้ายที่ไม่ได้ลงท้ายด้วย\nแม้ว่านี่จะหมายความว่าHello, World!และHello, World!\nมีการนับบรรทัดเดียวกัน (ซึ่งสำหรับฉันมีเหตุผล) มิฉะนั้นคุณสามารถเพิ่มลง1ในการนับ\nได้


0

ในการนับจำนวนบรรทัดคุณสามารถนับจำนวนการขึ้นบรรทัดใหม่ได้:

n_lines = sum(1 for s in the_string if s == "\n") + 1 # add 1 for last line

แก้ไข :

คำตอบอื่น ๆ ที่มีในตัวcountนั้นเหมาะสมกว่าจริงๆ


3
นอกเหนือจากเพียงแค่ใช้count, bools มี addable (ในความเป็นจริงพวกเขา subclass int) ดังนั้น genexp sum(s == "\n" for s in the_string)สามารถเขียนเป็น
lvc

ตอนนี้คุณแค่นับบรรทัดว่างเปล่า?
Thijs van Dien

ใช่ฉันไม่ทิ้งบรรทัดว่างไว้เลย
Jakub M.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.