14

How can you get the nth line of a string in Python 3? For example

getline("line1\nline2\nline3",3)

Is there any way to do this using stdlib/builtin functions? I prefer a solution in Python 3, but Python 2 is also fine.

0

9 Answers 9

30

Try the following:

s = "line1\nline2\nline3"
print s.splitlines()[2]
Sign up to request clarification or add additional context in comments.

3 Comments

I know about this solution. But this is memory ineffecient. Thanks for the answer.
The example you gave only had 17 characters in it, and you gave no indication that you know about splitlines or that the string was large. Please could you edit your question to make that clear, and then I'll delete this answer?
use print.splitlines()[-1] to get the last line.
5

a functional approach

>>> import StringIO
>>> from itertools import islice
>>> s = "line1\nline2\nline3"
>>> gen = StringIO.StringIO(s)
>>> print next(islice(gen, 2, 3))
line3

5 Comments

nice and short though how effecient is this?
I think its not very effecient because islice uses readline on files. readline stores the entire line in memory
@RamchandraApte, the string that you are looking to parse is already fully in memory. Additionally, islice works on iterators and has nothing to do with readline.
But I think my answer is better. I know it has nothing to do with readline. It calls readline indirectly. But it still uses a line more of memory.
@RamchandraApte: This solution is approximately 30% faster than yours. If you think that saving 80 bytes of memory is crucial to your application, that is on you. The reason cravoori's solution is faster is because most of the code is executed in C, while in your solution, more of the code is interpreted in Python. If you want to see for yourself, use the dis module to examine both.
5
`my_string.strip().split("\n")[-1]`

Comments

3

From the comments it seems as if this string is very large. If there is too much data to comfortably fit into memory one approach is to process the data from the file line-by-line with this:

N = ...
with open('data.txt') as inf:
    for count, line in enumerate(inf, 1):
        if count == N: #search for the N'th line
            print line

Using enumerate() gives you the index and the value of object you are iterating over and you can specify a starting value, so I used 1 (instead of the default value of 0)

The advantage of using with is that it automatically closes the file for you when you are done or if you encounter an exception.

2 Comments

I know about this too. I want the nth line of a string - not file. Thanks for the answer.
@RamchandraApte: Levon's solution works with strings too with one minor change. Change the with statement to with io.StringIO(data) as inf:
3

Use a string buffer:

import io    
def getLine(data, line_no):
    buffer = io.StringIO(data)
    for i in range(line_no - 1):
        try:
            next(buffer)
        except StopIteration:
            return '' #Reached EOF

    try:
        return next(buffer)
    except StopIteration:
        return '' #Reached EOF

6 Comments

I want a solution in Python 3 prefferably. I don't think .next() method is there for file objects in Python 3
@RamchandraApte Also, if you really prefer a v3 solution, it might be a good idea to explicitly mention this in your original post
@RamchandraApte Then just use next(buffer) instead of buffer.next() and io.StringIO instead of StringIO.StringIO
I have edited it to work with Python 3 and use readline() instead of next()
its for generators here we are using the file aspect of it
|
3

A more efficient solution than splitting the string would be to iterate over its characters, finding the positions of the Nth and the (N - 1)th occurence of '\n' (taking into account the edge case at the start of the string). The Nth line is the substring between those positions.

Here's a messy piece of code to demonstrate it (line number is 1 indexed):

def getLine(data, line_no):
    n = 0
    lastPos = -1
    for i in range(0, len(data) - 1):
        if data[i] == "\n":
            n = n + 1
            if n == line_no:
                return data[lastPos + 1:i]
            else:
                lastPos = i;



    if(n == line_no - 1):
        return data[lastPos + 1:]
    return "" # end of string

This is also more efficient than the solution which builds up the string one character at a time.

5 Comments

this is better and faster than the other solution
one problem it includes a line seperator: getLines("df\nd",2) = '\nd'
when this problem is fixed i will mark this question as the answer
Edited - it no longer includes the extra \n's.
i think my solution is better than this
1

Since you brought up the point of memory efficiency, is this any better:

s = "line1\nline2\nline3"

# number of the line you want
line_number = 2

i = 0
line = ''
for c in s:
   if i > line_number:
     break
   else:
     if i == line_number-1 and c != '\n':
       line += c
     elif c == '\n':
       i += 1

3 Comments

Great! This is exactly what I wanted. I was just creating a solution just like this!
my solution is better than this i think
Yeah, mine was a pretty brute force approach and not-so-clean code. This looks much better.
0

Wrote into two functions for readability

    string = "foo\nbar\nbaz\nfubar\nsnafu\n"

    def iterlines(string):
      word = ""
      for letter in string:
        if letter == '\n':
          yield word
          word = ""
          continue
        word += letter

    def getline(string, line_number):
      for index, word in enumerate(iterlines(string),1):
        if index == line_number:
          #print(word)
          return word

    print(getline(string, 4))

Comments

-3

My solution (effecient and compact):

def getLine(data, line_no):
    index = -1
    for _ in range(line_no):index = data.index('\n',index+1)
    return data[index+1:data.index('\n',index+1)]

1 Comment

Are you using punch cards? "Compact" hasn't been a virtue in programming for over 30 years; it also violates the spirit and letter of python.org/dev/peps/pep-0008

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.