2013-08-29 13 views
6

Chcę wcięcia wszystkich linii ciągu wielowierszowego oprócz pierwszego, bez owijania tekstu.Python: Wcięcie wszystkich linii ciągu poza pierwszym, przy jednoczesnym zachowaniu podziałów linii?

Na przykład, chcę włączyć:

A very very very very very very very very very very very very very very very very 
long mutiline 
string 

do:

A very very very very very very very very very very very very very very very very 
    long multiline 
    string 

Próbowałem

textwrap.fill(string, width=999999999999, subsequent_indent=' ',) 

Ale to nadal stawia cały tekst na jednej linii. Myśli?

+0

Z dokumentów dla 'previous_indent':" Łańcuch, który zostanie dodany do wszystkich wierszy zawijanych danych wyjściowych. " Ponieważ nic nie jest pakowane, nic nie jest wcięte. –

Odpowiedz

12

Wystarczy zastąpić znak nowego wiersza '\n' ze znakiem nowego wiersza plus spacji '\n    ' i zapisać go do zmiennej (od replace nie zmieni oryginalnego ciągu, ale zwróci nowy z zamiennikami).

string = string.replace('\n', '\n ') 
2

Czy masz na myśli coś takiego:

In [21]: s = 'abc\ndef\nxyz' 

In [22]: print s 
abc 
def 
xyz 

In [23]: print '\n '.join(s.split('\n')) 
abc 
    def 
    xyz 

?

edit: Alternatywnie (HT @Steven Rumbalski):

In [24]: print s.replace('\n', '\n ') 
abc 
    def 
    xyz 
+3

Dlaczego 'split' i' join' kiedy możesz po prostu 'replace'? –

+1

@ DavidY.Stephenson: Wydaje się, że używanie tekstu 'textwrap' dla pracy, która nie wymaga owijania, wydaje się być niepythoniczne. –

0

Nagie zastąpić wspomniane przez @steven-rumbalski będzie najskuteczniejszym sposobem do osiągnięcia tego celu, ale nie jest to jedyny sposób.

Oto kolejne rozwiązanie wykorzystujące wyliczenia list. Jeśli tekst został już podzielony na liście linii, będzie to znacznie szybciej niż bieganie join(), replace() i splitlines()

text = """A very very very very very very very very very very very very very very very very 
long mutiline 
string""" 

lines = text.splitlines() 
indented = [' ' + l for l in lines] 
indented[0] = lines[0] 
indented = '\n'.join(indented) 

lista może być modyfikowana w miejscu, ale jest znaczącym kosztem wydajności w porównaniu z wykorzystaniem drugiego zmienna. Jest także umiarkowanie szybsze wcięcie wszystkich linii, a następnie zamiana pierwszego wiersza w innej operacji.

Istnieje również moduł textwrap. Nie zgadzam się z tym, że używanie textwrap do wcięcia jest niepythoniczne. Jeśli linie są połączone w jeden ciąg zawierający znaki nowej linii, ten ciąg jest z natury zawijany. Wcięcie jest logicznym przedłużeniem zawijania tekstu, więc tekst jest dla mnie sensowny.

Z tym że jest wolny. Naprawdę, naprawdę wolno. Jak 15 razy wolniej.

Python 3 dodano indent do textwrap, co sprawia, że ​​wcięcia bez ponownego owijania są bardzo łatwe. Z pewnością jest bardziej elegancki sposób obsługi orzeczenia lambda, ale robi to dokładnie to, o co prosiło pierwotne pytanie.

indented = textwrap.indent(text, ' ', lambda x: not text.splitlines()[0] in x) 

Oto kilka timeit wyniki różnych metod.

>>> timeit.timeit(r"text.replace('\n', '\n ')", setup='text = """%s"""' % text) 
0.5123521030182019 

dwóch list rozwiązań ze zrozumieniem:

>>> timeit.timeit(r"indented = [' ' + i for i in lines]; indented[0] = lines[0]", setup='lines = """%s""".splitlines()' % text) 
0.7037646849639714 

>>> timeit.timeit(r"indented = [lines[0]] + [' ' + i for i in lines[1:]]", setup='lines = """%s""".splitlines()' % text) 
1.0310905870283023 

i tu jest niefortunne textwrap wynik:

>>> timeit.timeit(r"textwrap.indent(text, ' ', lambda x: not text.splitlines()[0] in x)", setup='import textwrap; text = """%s"""' % text) 
7.7950868209591135 

Myślałem trochę tego czasu może być strasznie nieskuteczny orzecznik, ale nawet z usunięty, jest nadal ponad 8 razy wolniejszy niż samodzielny zamiennik.

>>> timeit.timeit(r"textwrap.indent(text, ' ')", setup='import textwrap; text = """%s"""' % text) 
4.266149697010405 
Powiązane problemy