Есть ли простой способ узнать, на каком номере строки находится указатель файла? - PullRequest
8 голосов
/ 16 июня 2011

В Python 2.5 я читаю файл данных структурированного текста (размером ~ 30 МБ), используя указатель файла:

fp = open('myfile.txt', 'r')
line = fp.readline()
# ... many other fp.readline() processing steps, which
# are used in different contexts to read the structures

Но затем, анализируя файл, я наткнулся на что-то интересное, о чем я хочу сообщить номер строки, чтобы я мог исследовать файл в текстовом редакторе. Я могу использовать fp.tell(), чтобы сказать мне, где находится смещение байта (например, 16548974L), но нет "fp.tell_line_number ()", чтобы помочь мне перевести это на номер строки.

Есть ли встроенный в Python или расширение, чтобы легко отслеживать и "сообщать", на каком номере строки находится указатель текстового файла?

Примечание: я не прошу использовать счетчик стиля line_number += 1, как я называю fp.readline() в различных контекстах, и этот подход потребует больше отладки, чем стоит вставить счетчик в правые углы кода.

Ответы [ 8 ]

14 голосов
/ 16 июня 2011

Типичное решение этой проблемы - определить новый класс, который оборачивает существующий экземпляр file, который автоматически подсчитывает числа. Примерно так (чуть выше головы, я этого не проверял):

class FileLineWrapper(object):
    def __init__(self, f):
        self.f = f
        self.line = 0
    def close(self):
        return self.f.close()
    def readline(self):
        self.line += 1
        return self.f.readline()
    # to allow using in 'with' statements 
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

Используйте это так:

f = FileLineWrapper(open("myfile.txt", "r"))
f.readline()
print(f.line)

Похоже, что стандартный модуль fileinput делает то же самое (и некоторые другие вещи); Вы можете использовать это вместо этого, если хотите.

11 голосов
/ 16 июня 2011

Может оказаться полезным модуль fileinput. Он предоставляет обобщенный интерфейс для перебора произвольного числа файлов. Некоторые важные моменты из документов:

fileinput.lineno()

Возвращает совокупный номер строки, которую только что прочитали. Перед прочтением первой строки возвращает 0. После прочтения последней строки последнего файла возвращает номер строки этой строки.

fileinput.filelineno()

Возвращает номер строки в текущем файле. Перед прочтением первой строки возвращает 0. После прочтения последней строки последнего файла возвращает номер строки этой строки в файле.

9 голосов
/ 25 сентября 2016

Следующий код будет печатать номер строки (там, где в данный момент находится указатель) при обходе файла ('testfile')

file=open("testfile", "r")
for line_no, line in enumerate(file):
    print line_no     # The content of the line is in variable 'line'
file.close()

output:

1
2
3
...
1 голос
/ 16 июня 2011

Я так не думаю, не так, как вы хотите (как в стандартной встроенной функции дескрипторов файлов Python, возвращаемых open).

Если вы не поддаются отслеживаниюномер строки вручную, когда вы читаете строки или используете класс-оболочку (кстати, отличные предложения от GregH и senderle), тогда я думаю, что вам придется просто использовать цифру fp.tell() и вернуться к началуфайла, читая, пока не дойдете до него.

Это не слишком плохой вариант, так как я предполагаю, что условия ошибки будут менее вероятными, чем все, что работает плавно.Если все работает хорошо, никакого влияния нет.

Если есть ошибка, то у вас есть дополнительные усилия для повторного сканирования файла.Если файл большой, , который может повлиять на вашу воспринимаемую производительность - вы должны принять это во внимание, если это проблема.

0 голосов
/ 06 февраля 2018

Недавно возился с подобной проблемой и придумал это решение на основе классов.

class TextFileProcessor(object):

    def __init__(self, path_to_file):
        self.print_line_mod_number = 0
        self.__path_to_file = path_to_file
        self.__line_number = 0

    def __printLineNumberMod(self):
        if self.print_line_mod_number != 0:
            if self.__line_number % self.print_line_mod_number == 0:
                print(self.__line_number)

    def processFile(self):
        with open(self.__path_to_file, 'r', encoding='utf-8') as text_file:
            for self.__line_number, line in enumerate(text_file, start=1):
                self.__printLineNumberMod()

                # do some stuff with line here.

Установите для свойства print_line_mod_number частоту, которую вы хотите зарегистрировать, а затем вызовите processFile.

Например ... если вы хотите получить обратную связь через каждые 100 строк, она будет выглядеть следующим образом.

tfp = TextFileProcessor('C:\\myfile.txt')
tfp.print_line_mod_number = 100
tfp.processFile()

Вывод на консоль будет

100
200
300
400
etc...
0 голосов
/ 05 января 2016

Что касается решения от @ eyquem , я предлагаю использовать mode='r' с модулем fileinput и опцией fileinput.lineno(), и это сработало для меня.

Вот как я реализуюэти опции в моем коде.

    table=fileinput.input('largefile.txt',mode="r")
    if fileinput.lineno() >= stop : # you can disregard the IF condition but I am posting to illustrate the approach from my code.
           temp_out.close()
0 голосов
/ 16 июня 2011

Следующий код создает функцию Which_Line_for_Position (pos) , которая дает номер строки для позиции pos , то есть номер строки , в которой находится символ, расположенный в файле в позиции pos .

Эта функция может использоваться с любой позицией в качестве аргумента, независимо от значения файлаТекущее положение указателя и из истории перемещений этого указателя до вызова функции.

Таким образом, с этой функцией не ограничивается определение номера текущей строки только во время непрерывной итерации полинии, как в случае с решением Грега Хьюгилла.

with open(filepath,'rb') as f:
    GIVE_NO_FOR_END = {}
    end = 0
    for i,line in enumerate(f):
        end += len(line)
        GIVE_NO_FOR_END[end] = i
    if line[-1]=='\n':
        GIVE_NO_FOR_END[end+1] = i+1
    end_positions = GIVE_NO_FOR_END.keys()
    end_positions.sort()

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

.

Это же решение можно записать с помощью модуля fileinput :

import fileinput

GIVE_NO_FOR_END = {}
end = 0
for line in fileinput.input(filepath,'rb'):
    end += len(line)
    GIVE_NO_FOR_END[end] = fileinput.filelineno()
if line[-1]=='\n':
    GIVE_NO_FOR_END[end+1] = fileinput.filelineno()+1
fileinput.close()

end_positions = GIVE_NO_FOR_END.keys()
end_positions.sort()

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

Но у этого решения есть некоторые неудобства:

  • необходимо импортировать модуль fileinput
  • оно удаляет все контекстНет файла!Должно быть что-то не так в моем коде, но я не знаю fileinput достаточно, чтобы найти это.Или это нормальное поведение функции fileinput.input () ?
  • . Кажется, что файл сначала полностью читается, прежде чем можно будет запустить любую итерацию.Если это так, для файла очень большой, размер файла может превышать объем оперативной памяти.Я не уверен в этом: я пытался протестировать файл размером 1,5 ГБ, но он довольно длинный, и на данный момент я упустил этот пункт.Если этот пункт верен, он представляет собой аргумент для использования другого решения с enumerate ()

.

Пример:

text = '''Harold Acton (1904–1994)
Gilbert Adair (born 1944)
Helen Adam (1909–1993)
Arthur Henry Adams (1872–1936)
Robert Adamson (1852–1902)
Fleur Adcock (born 1934)
Joseph Addison (1672–1719)
Mark Akenside (1721–1770)
James Alexander Allan (1889–1956)
Leslie Holdsworthy Allen (1879–1964)
William Allingham (1824/28-1889)
Kingsley Amis (1922–1995)
Ethel Anderson (1883–1958)
Bruce Andrews (born 1948)
Maya Angelou (born 1928)
Rae Armantrout (born 1947)
Simon Armitage (born 1963)
Matthew Arnold (1822–1888)
John Ashbery (born 1927)
Thomas Ashe (1836–1889)
Thea Astley (1925–2004)
Edwin Atherstone (1788–1872)'''


#with open('alao.txt','rb') as f:

f = text.splitlines(True)
# argument True in splitlines() makes the newlines kept

GIVE_NO_FOR_END = {}
end = 0
for i,line in enumerate(f):
    end += len(line)
    GIVE_NO_FOR_END[end] = i
if line[-1]=='\n':
    GIVE_NO_FOR_END[end+1] = i+1
end_positions = GIVE_NO_FOR_END.keys()
end_positions.sort()


print '\n'.join('line %-3s  ending at position %s' % (str(GIVE_NO_FOR_END[end]),str(end))
                for end in end_positions)

def Which_Line_for_Position(pos,
                            dic = GIVE_NO_FOR_END,
                            keys = end_positions,
                            kmax = end_positions[-1]):
    return dic[(k for k in keys if pos < k).next()] if pos<kmax else None

print
for x in (2,450,320,104,105,599,600):
    print 'pos=%-6s   line %s' % (x,Which_Line_for_Position(x))

результат

line 0    ending at position 25
line 1    ending at position 51
line 2    ending at position 74
line 3    ending at position 105
line 4    ending at position 132
line 5    ending at position 157
line 6    ending at position 184
line 7    ending at position 210
line 8    ending at position 244
line 9    ending at position 281
line 10   ending at position 314
line 11   ending at position 340
line 12   ending at position 367
line 13   ending at position 393
line 14   ending at position 418
line 15   ending at position 445
line 16   ending at position 472
line 17   ending at position 499
line 18   ending at position 524
line 19   ending at position 548
line 20   ending at position 572
line 21   ending at position 600

pos=2        line 0
pos=450      line 16
pos=320      line 11
pos=104      line 3
pos=105      line 4
pos=599      line 21
pos=600      line None

.

Тогда, имея функцию Which_Line_for_Position () , легко получить номер текущей строки: просто пропустив f.tell () в качестве аргумента функции

Но ПРЕДУПРЕЖДЕНИЕ : при использовании f.tell () и перемещении указателя файла вфайл, абсолютно необходимо, чтобы файл был открыт в двоичном режиме: 'rb' или 'rb +' или 'ab' или ....

0 голосов
/ 16 июня 2011

Одним из способов может быть итерация по строке и ведение явного подсчета количества уже увиденных строк:

>>> f=open('text.txt','r')
>>> from itertools import izip
>>> from itertools import count
>>> f=open('test.java','r')
>>> for line_no,line in izip(count(),f):
...     print line_no,line
...