Quando si divide una stringa vuota in Python, perché split () restituisce un elenco vuoto mentre split ('\ n') restituisce ['']?


155

Sto usando split('\n')per ottenere le linee in una stringa e ho scoperto che ''.split()restituisce un elenco vuoto [], mentre ''.split('\n')restituisce ['']. C'è qualche motivo specifico per tale differenza?

E c'è un modo più conveniente per contare le linee in una stringa?


Risposte:


247

Domanda: sto usando split ('\ n') per ottenere linee in una stringa e ho scoperto che '' .split () restituisce un elenco vuoto [], mentre '' .split ('\ n') restituisce [''] .

Il metodo str.split () ha due algoritmi. Se non viene fornito alcun argomento, si divide su ripetute esecuzioni di spazi bianchi. Tuttavia, se viene fornito un argomento, viene trattato come un unico delimitatore senza ripetizioni.

Nel caso di dividere una stringa vuota, la prima modalità (nessun argomento) restituirà un elenco vuoto perché lo spazio bianco viene mangiato e non ci sono valori da inserire nell'elenco dei risultati.

Al contrario, la seconda modalità (con un argomento come \n) produrrà il primo campo vuoto. Considera se avessi scritto '\n'.split('\n'), otterrai due campi (uno diviso, ti dà due metà).

Domanda: c'è qualche motivo specifico per tale differenza?

Questa prima modalità è utile quando i dati sono allineati in colonne con quantità variabili di spazi bianchi. Per esempio:

>>> 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']

La seconda modalità è utile per i dati delimitati come CSV in cui le virgole ripetute indicano campi vuoti. Per esempio:

>>> 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']

Nota, il numero di campi risultato è uno maggiore del numero di delimitatori. Pensa di tagliare una corda. Se non fai tagli, hai un pezzo solo. Fare un taglio, dà due pezzi. Fare due tagli, dà tre pezzi. E così è con il metodo str.split (delimitatore) di Python :

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

Domanda: esiste un modo più conveniente per contare le linee in una stringa?

Sì, ci sono un paio di modi semplici. Uno usa str.count () e l'altro usa str.splitlines () . Entrambi i modi daranno la stessa risposta a meno che non manchi la riga finale \n. Se manca la riga finale, l' approccio str.splitlines fornirà la risposta esatta. Una tecnica più veloce che è anche accurata utilizza il metodo di conteggio ma poi lo corregge per la nuova riga finale:

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

Domanda di @Kaz: perché diamine ci sono due algoritmi molto diversi messi insieme in un'unica funzione?

La firma per str.split ha circa 20 anni e un certo numero di API di quell'epoca sono strettamente pragmatiche. Anche se non perfetto, la firma del metodo non è nemmeno "terribile". Per la maggior parte, le scelte di progettazione API di Guido hanno superato la prova del tempo.

L'API corrente non è priva di vantaggi. Considera stringhe come:

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

Quando viene chiesto di suddividere queste stringhe in campi, le persone tendono a descrivere entrambe usando la stessa parola inglese "split". Quando viene chiesto di leggere codice come fields = line.split() o fields = line.split(','), le persone tendono a interpretare correttamente le istruzioni come "divide una riga in campi".

Lo strumento di testo in colonne di Microsoft Excel ha fatto una scelta API simile e incorpora entrambi gli algoritmi di divisione nello stesso strumento. Le persone sembrano modellare mentalmente la divisione dei campi come un singolo concetto anche se è coinvolto più di un algoritmo.


28

Sembra essere semplicemente il modo in cui dovrebbe funzionare, secondo la documentazione :

La divisione di una stringa vuota con un separatore specificato restituisce [''].

Se sep non è specificato o è None, viene applicato un diverso algoritmo di suddivisione: le esecuzioni di spazi bianchi consecutivi sono considerate come un singolo separatore e il risultato non conterrà stringhe vuote all'inizio o alla fine se la stringa ha spazi bianchi iniziali o finali. Di conseguenza, la divisione di una stringa vuota o di una stringa costituita da spazi bianchi con un separatore None restituisce [].

Quindi, per renderlo più chiaro, la split()funzione implementa due diversi algoritmi di suddivisione e utilizza la presenza di un argomento per decidere quale eseguire. Ciò potrebbe essere dovuto al fatto che consente di ottimizzare quello per nessun argomento più di quello con argomenti; Non lo so.


4

.split()senza parametri cerca di essere intelligente. Si divide su qualsiasi spazio bianco, tabulazione, spazio, avanzamento riga, ecc. E, di conseguenza, salta tutte le stringhe vuote.

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

In sostanza, .split()senza parametri vengono utilizzati per estrarre parole da una stringa, al contrario di.split() parametri che prendono solo una stringa e la dividono.

Questa è la ragione della differenza.

E sì, contare le linee per divisione non è un modo efficiente. Contare il numero di avanzamenti riga e aggiungerne uno se la stringa non termina con un avanzamento riga.


2

Utilizzare count():

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

4
Il +1 dovrebbe essere fatto solo se il testo non termina con '\ n'.
Lennart Regebro,

8
Bene, se termina con "\ n", l'ultima riga è vuota. Sebbene inutile, conta comunque come linea, no?
Jakub M.

2
no. quando scrivo 3 righe di testo in un file e finisco ognuna con un avanzamento riga, allora direi che il file contiene 3 righe. su unix è consigliabile avere sempre un file di testo che termina con un avanzamento riga. altrimenti cat filealtera la riga di comando e si lamenta sovversione. vi aggiunge sempre uno.
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.

Nota l'ultima frase.

Per contare le linee puoi semplicemente contare quante \nce ne sono:

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

L'ultima parte prende in considerazione l'ultima riga che non terminano con \n, anche se questo significa che Hello, World!e Hello, World!\nhanno lo stesso numero di linee (che per me è ragionevole), altrimenti si può semplicemente aggiungere 1al conte di \n.


0

Per contare le linee, puoi contare il numero di interruzioni di linea:

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

Modifica :

L'altra risposta con built-in countè più adatta, in realtà


3
Oltre al solo utilizzo count, i bool sono addizionabili (in effetti, sono sottoclassati int), quindi il genexp può essere scritto come sum(s == "\n" for s in the_string).
PVC

In questo momento stai contando solo righe vuote?
Thijs van Dien,

Sì, non scarto nessuna riga vuota
Jakub M.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.