Проблема заключается в том, что кто-то изменил URL-адрес, не сообщив нам, и они установили перенаправление на место. HttpClient отвечает на переадресацию, но фактически отправляет запрос в конечный пункт назначения как Get.
Мне кажется, что в HttpClient мне кажется, что он должен либо отправить окончательный запрос как сообщение , или бросить исключение, говоря, что он не может делать то, что я просил.
Код я закончил тем, что использовал. Я думаю, что это является лучшим до сих пор:
def tail(f, n, offset=None):
"""Reads a n lines from f with an offset of offset lines. The return
value is a tuple in the form ``(lines, has_more)`` where `has_more` is
an indicator that is `True` if there are more lines in the file.
"""
avg_line_length = 74
to_read = n + (offset or 0)
while 1:
try:
f.seek(-(avg_line_length * to_read), 2)
except IOError:
# woops. apparently file is smaller than what we want
# to step back, go to the beginning instead
f.seek(0)
pos = f.tell()
lines = f.read().splitlines()
if len(lines) >= to_read or pos == 0:
return lines[-to_read:offset and -offset or None], \
len(lines) > to_read or pos > 0
avg_line_length *= 1.3
Это может быть более быстро, чем Ваш. Не делает предположений о длине строки. Спины через файл один блок за один раз, пока это не нашло правильное количество '\n' символами.
def tail( f, lines=20 ):
total_lines_wanted = lines
BLOCK_SIZE = 1024
f.seek(0, 2)
block_end_byte = f.tell()
lines_to_go = total_lines_wanted
block_number = -1
blocks = [] # blocks of size BLOCK_SIZE, in reverse order starting
# from the end of the file
while lines_to_go > 0 and block_end_byte > 0:
if (block_end_byte - BLOCK_SIZE > 0):
# read the last block we haven't yet read
f.seek(block_number*BLOCK_SIZE, 2)
blocks.append(f.read(BLOCK_SIZE))
else:
# file too small, start from begining
f.seek(0,0)
# only read what was not read
blocks.append(f.read(block_end_byte))
lines_found = blocks[-1].count('\n')
lines_to_go -= lines_found
block_end_byte -= BLOCK_SIZE
block_number -= 1
all_read_text = ''.join(reversed(blocks))
return '\n'.join(all_read_text.splitlines()[-total_lines_wanted:])
мне не нравятся хитрые предположения о длине строки, когда - на практике - Вы никогда не можете знать подобные вещи.
Обычно это определит местоположение последних 20 строк на первой или второй передаче через цикл. Если Ваши 74 символьных вещи на самом деле точны, Вы делаете размер блока 2048, и Вы выследите 20 строк почти сразу.
кроме того, я не сжигаю много мозговых калорий, пытающихся ловко обходить выравнивание с физическими блоками ОС. Используя эти высокоуровневые пакеты ввода-вывода, я сомневаюсь, что Вы будете видеть любое последствие производительности попытки выровняться на границах блока ОС. Если Вы используете ввод-вывод низшего уровня, то Вы могли бы видеть ускорение.
Вообще-то, если задуматься это, вероятно, настолько же быстро как что-либо здесь.
def tail( f, window=20 ):
lines= ['']*window
count= 0
for l in f:
lines[count%window]= l
count += 1
print lines[count%window:], lines[:count%window]
Это намного более просто. И это, действительно кажется, разрывается вперед в хорошем темпе.
Для эффективности с очень большими файлами (распространенный в ситуациях с файлом журнала, где можно хотеть использовать хвост), Вы обычно не хотите читать целый файл (даже если Вы действительно делаете это, не читая целый файл в память сразу), Однако, действительно необходимо так или иначе разработать смещение в строках, а не символах. Одна возможность читает назад с, ищут () символ символом, но это очень медленно. Вместо этого лучше для обработки в больших блоках.
у меня есть служебная функция, которую я записал только что для чтения файлов назад, которые могут использоваться здесь.
import os, itertools
def rblocks(f, blocksize=4096):
"""Read file as series of blocks from end of file to start.
The data itself is in normal order, only the order of the blocks is reversed.
ie. "hello world" -> ["ld","wor", "lo ", "hel"]
Note that the file must be opened in binary mode.
"""
if 'b' not in f.mode.lower():
raise Exception("File must be opened using binary mode.")
size = os.stat(f.name).st_size
fullblocks, lastblock = divmod(size, blocksize)
# The first(end of file) block will be short, since this leaves
# the rest aligned on a blocksize boundary. This may be more
# efficient than having the last (first in file) block be short
f.seek(-lastblock,2)
yield f.read(lastblock)
for i in range(fullblocks-1,-1, -1):
f.seek(i * blocksize)
yield f.read(blocksize)
def tail(f, nlines):
buf = ''
result = []
for block in rblocks(f):
buf = block + buf
lines = buf.splitlines()
# Return all lines except the first (since may be partial)
if lines:
result.extend(lines[1:]) # First line may not be complete
if(len(result) >= nlines):
return result[-nlines:]
buf = lines[0]
return ([buf]+result)[-nlines:]
f=open('file_to_tail.txt','rb')
for line in tail(f, 20):
print line
[Редактирование] Добавленная более определенная версия (избегает потребности инвертировать дважды)
Если чтение целого файла приемлемо, тогда используют двухстороннюю очередь.
from collections import deque
deque(f, maxlen=n)
До 2,6, двухсторонние очереди не имели maxlen опции, но достаточно легко реализовать.
import itertools
def maxque(items, size):
items = iter(items)
q = deque(itertools.islice(items, size))
for item in items:
del q[0]
q.append(item)
return q
, Если это - требование для чтения файла из конца, затем используйте галоп (иначе экспоненциал) поиск.
def tail(f, n):
assert n >= 0
pos, lines = n+1, []
while len(lines) <= n:
try:
f.seek(-pos, 2)
except IOError:
f.seek(0)
break
finally:
lines = list(f)
pos *= 2
return lines[-n:]
Принимает подобную Unix систему на Python 2, который можно сделать:
import os
def tail(f, n, offset=0):
stdin,stdout = os.popen2("tail -n "+n+offset+" "+f)
stdin.close()
lines = stdout.readlines(); stdout.close()
return lines[:,-offset]
Для python 3 можно сделать:
import subprocess
def tail(f, n, offset=0):
proc = subprocess.Popen(['tail', '-n', n + offset, f], stdout=subprocess.PIPE)
lines = proc.stdout.readlines()
return lines[:, -offset]
Другое Решение
, если Ваш txt файл похож на это: волкодав ящерицы кошки змею мыши
Вы могли инвертировать этот файл путем простого использования индексации массива в Python '' '
contents=[]
def tail(contents,n):
with open('file.txt') as file:
for i in file.readlines():
contents.append(i)
for i in contents[:n:-1]:
print(i)
tail(contents,-5)
результат: кошка ящерицу волка собаки
на основе самого популярного ответа С.Лотта (25 сентября '08, 21:43), но исправлено для небольших файлов.
def tail(the_file, lines_2find=20):
the_file.seek(0, 2) #go to end of file
bytes_in_file = the_file.tell()
lines_found, total_bytes_scanned = 0, 0
while lines_2find+1 > lines_found and bytes_in_file > total_bytes_scanned:
byte_block = min(1024, bytes_in_file-total_bytes_scanned)
the_file.seek(-(byte_block+total_bytes_scanned), 2)
total_bytes_scanned += byte_block
lines_found += the_file.read(1024).count('\n')
the_file.seek(-total_bytes_scanned, 2)
line_list = list(the_file.readlines())
return line_list[-lines_2find:]
#we read at least 21 line breaks from the bottom, block by block for speed
#21 to ensure we don't get a half line
Надеюсь, это будет полезно.