在Python中获取字符串的第n行

12

如何在Python 3中获取字符串的第n行?比如说:

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

是否有使用stdlib / builtin函数的方法来实现此功能? 我更喜欢用Python 3解决方案,但Python 2也可以。

9个回答

28

尝试以下内容:

s = "line1\nline2\nline3"
print s.splitlines()[2]

我知道这个解决方案。 但是它会浪费内存。 感谢您的回答。 - Ramchandra Apte
你提供的示例只有17个字符,并且你没有表明你知道splitlines或者字符串很大。请编辑你的问题,让这一点清晰明了,然后我会删除这个回答。 - Mark Longair
使用 print.splitlines()[-1] 获取最后一行。 - George Horlacher

5
`my_string.strip().split("\n")[-1]`

5
一种函数式的方法
>>> import StringIO
>>> from itertools import islice
>>> s = "line1\nline2\nline3"
>>> gen = StringIO.StringIO(s)
>>> print next(islice(gen, 2, 3))
line3

这很简洁,但效率如何? - Ramchandra Apte
我认为这不是很有效的,因为islice在文件上使用readline。readline会将整行存储在内存中。 - Ramchandra Apte
2
@RamchandraApte,您要解析的字符串已经完全存储在内存中。此外,islice适用于迭代器,并与readline无关。 - iruvar
但我认为我的答案更好。我知道它与readline无关。它间接调用了readline。 但它仍然会使用更多的内存空间。 - Ramchandra Apte
2
@RamchandraApte:这个解决方案比你的快大约30%。如果你认为节省80字节的内存对你的应用程序至关重要,那就由你决定。Cravoori的解决方案之所以更快,是因为大部分代码是在C中执行的,而在你的解决方案中,更多的代码是在Python中解释的。如果你想亲自验证,请使用“dis”模块检查两者。 - Joel Cornett

3

从评论中看来,这个字符串非常大。如果数据太多而无法舒适地放入内存中,一种方法是使用以下方法逐行处理文件中的数据:

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

使用enumerate()函数可以获取正在迭代的对象的索引和值,并且您可以指定起始值,因此我使用了1(而不是默认值0)。
使用with的优点是当您完成或遇到异常时,它会自动为您关闭文件。

我也知道这个。我想要一个字符串的第n行 - 不是文件。 谢谢答复。 - Ramchandra Apte
2
@RamchandraApte:Levon的解决方案也适用于字符串,只需进行一些小修改。将with语句更改为with io.StringIO(data) as inf: - Joel Cornett

3

使用字符串缓冲区:

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

我希望用Python 3来解决问题。我认为在Python 3中,文件对象没有.next()方法。 - Ramchandra Apte
3
@RamchandraApte 如果你真的更喜欢使用 v3 解决方案,最好在原帖中明确提出。 - Levon
@RamchandraApte 那么只需使用 next(buffer) 而不是 buffer.next(),并且使用 io.StringIO 而不是 StringIO.StringIO - sloth
我已经编辑过它,使其适用于Python 3,并使用readline()代替next()。 - Ramchandra Apte
这是关于生成器的内容,我们在这里使用它的文件方面。 - Ramchandra Apte
@RamchandraApte:这是针对迭代器的。它可以工作。文件对象和StringIO对象都具有迭代器功能。 - Joel Cornett

3
比起分割字符串,更高效的解决方案是迭代字符串中的字符,找到第N和第(N-1)次出现'\n'的位置(考虑到字符串开头的边界情况)。N行是两个位置之间的子串。
以下是一个示例代码(行号从1开始):
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

这种方法比逐个字符构建字符串的解决方案更有效率。

这个解决方案比另一个更好更快。 - Ramchandra Apte
一个问题是它包含了一个换行符:getLines("df\nd",2) = '\nd' - Ramchandra Apte
当这个问题被解决后,我会将这个问题标记为答案。 - Ramchandra Apte
编辑 - 现在不再包含额外的 \n。 - QuantumBadger
我认为我的解决方案比这个更好。 - Ramchandra Apte

1

既然您提到了内存效率这一点,那么这个方案是否更好呢:

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

太好了!这正是我想要的。我正好在创建一个和这个解决方案一样的东西! - Ramchandra Apte
我认为我的解决方案比这个更好。 - Ramchandra Apte
是的,我的方法相当暴力,并且代码不够简洁。这个看起来好多了。 - vaidik

0

为了提高可读性,将其拆分成两个函数

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

-3

我的解决方案(高效且紧凑):

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

12
你是否在使用打孔卡?“紧凑”对于编程来说已经超过30年不再是一种优点;这也违反了http://www.python.org/dev/peps/pep-0008/的精神和文字。 - msw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接