简体   繁体   中英

What is the most pythonic way of printing only certain lines of a string?

Suppose I have a string (not a file) that spans many lines:

multiline_string = '''I met a traveller from an antique land
Who said: Two vast and trunkless legs of stone
Stand in the desert... near them, on the sand,
Half sunk, a shattered visage lies, whose frown,
And wrinkled lip, and sneer of cold command,
Tell that its sculptor well those passions read
Which yet survive, stamped on these lifeless things,
The hand that mocked them and the heart that fed;

And on the pedestal these words appear:
'My name is Ozymandias, king of kings;
Look on my works, ye Mighty, and despair!'
Nothing beside remains. Round the decay
Of that colossal wreck, boundless and bare
The lone and level sands stretch far away.'''

I want to get only certain lines of the string, as a single string (not as a list of strings). One way of doing it is this:

pedestal_lines = "\n".join(multiline_string.splitlines()[9:12])
print(pedestal_lines)

Output:

And on the pedestal these words appear:
'My name is Ozymandias, king of kings;
Look on my works, ye Mighty, and despair!'

But that way is not very good: it has to split the string into a list of strings, index this list, then join the lists back together with the str.join() method. Not to mention, it's ugly-looking and not very readable. Is there a more elegant/pythonic way of achieving this?

If you don't want to split the string, you can do the following:

  • use regexes to capture 3 lines after 8 lines
  • count the positions of the linefeeds and slice the string just once with the proper positions

You'll forgive the one-off errors that I may have done in the code below.

Regex :

import re

print(re.sub("^(.*\n){8}((?:.*\n){3})(.*\n){1,}",r"\2",multiline_string))

(create a group of 8 lines, then a group of 3 lines, then the rest, replace by the second group)

Position extract + slicing :

linefeed_pos = [i for i,c in enumerate(multiline_string) if c=="\n"]
print(multiline_string[linefeed_pos[7]:linefeed_pos[11]])

(extract the position of the linefeed chars with list comprehension on the original string, then slice using those line-indexed positions). The drawback of this approach is that it computes all the indexes, not only until the upper line bound. That can be easily fixed by wrapping a generator comprehension in a list comprehension to stop just when the indices are no longer needed:

linefeed_pos = [next (i for i,c in enumerate(multiline_string) if c=="\n") for _ in range(12)]

Maybe one slicing/extract is better than splitting & joining for performance (I understand that seeing a big list going to waste just to pick 3 lines is unbearable), but I wouldn't call that pythonic.

Both methods explained above should be faster than yours if you have a lot of lines if performance/memory matters. If it doesn't, then stick to your solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM