Разное поведение из-за разных "стилей зацикливания" - PullRequest
3 голосов
/ 12 апреля 2011

У меня простая проблема. Перейдите к определенной строке штрафа и удалите все после этого. Я использую подходящий вызов file.truncate (). Однако два фрагмента кода ниже ведут себя по-разному.

1)

with open(file, "a+b", 1) as f:
  #Navigate to the MARKER
  while True:
    line = f.readline()
    if MARKER in line:
      f.truncate()
      f.write(stuff)
      break

2)

with open(file, "a+b", 1) as f:
  #Navigate to the MARKER
  for line in f:
    if MARKER in line:
      f.truncate()
      f.write(stuff)
      break

(1) ведет себя как ожидалось. Однако в случае (2) файл в усеченных нескольких строках после МАРКЕРА найден. Я предполагаю, что происходит некоторая буферизация, но, как вы можете видеть, я явно определяю поведение буферизации как «буферизацию строки» для вызова open ().

Есть мысли? Я хотел бы использовать более интуитивный синтаксис "для строки в файле" ...

Ответы [ 4 ]

3 голосов
/ 12 апреля 2011

Подсказка, кажется, находится в этом бите источника C Python - Python 2.7, кажется, использует буфер чтения заголовка 8 КБ для for line in file:.

2 голосов
/ 12 апреля 2011

С Документация Python , 5. Встроенные типы / 5.9. Файловые объекты:

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

Кстати: обычно не рекомендуется использовать ключевые слова (например, file) в качестве имен переменных.

2 голосов
/ 12 апреля 2011

Обычно оператор типа for x in y ожидает, что y не изменится в цикле. Вы нарушаете этот контракт.

0 голосов
/ 12 апреля 2011

Это из-за режима 'a':

а

Открыть для добавления (запись в конце файла). Файл создается, если он не существует. Поток помещается в конец файла .

а +

Открыть для чтения и добавления (запись в конце файла). Файл создан, если он не существует. исходная позиция файла для чтения в начале файла, но Вывод всегда добавляется в конец файла .

http://linux.die.net/man/3/fopen

.

EDIT

Мой ответ выше неверен.

Я уже знал, что зацикливание строк файла использует упреждающее чтение буфера, но я полагал, что truncate () вызовет перемещение указателя файла в конец файла, поскольку, насколько мне известно, усечение файла заключается в записи небольшой последовательности байтов, называемой EOF, означающей конец файла, а режим 'a' всегда провоцирует запись в конец файла независимо от положения указателя файла. незадолго до момента написания.

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

Но отрицательное голосование без каких-либо объяснений является потертым и разочаровывающим в том случае, когда ошибка в этом ответе не очевидна.

.

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

Чтобы прояснить ситуацию, файл 'fileA' состоит из строк, каждая из которых имеет длину 100 символов (в том числе '\ r \ n), заканчивающихся так (' \ r \ n 'здесь не видны):

....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000100
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000200
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000300
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000400
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000500
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000600
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000700
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000800
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00000900
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00001000
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00001100
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00001200
....ffffffgggggggggghhhhhhhhhhiiiiiiiiii00001300
............................

код:

print '\n===================== 1 ==================\n'

from os.path import getsize

# length of ecr is 90 :
ecr = 10*'a' + 10*'b' + 10*'c' + 10*'d' + 10*'e' +\
      10*'f' + 10*'g' + 10*'h' + 10*'i'


# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # Length of each written line is 100 :
        # 90 (ecr) + 8 (str(i).zfill(8)) + 2 ('\r\n')
        # File's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
# truncating file at uncontroled position
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00000800' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # in the file if there wasn't a reading buffer
            g.truncate()
print 'size of fileA after truncating : ',getsize('fileA.txt')

результат

===================== 1 ==================

size of fileA before truncating :  53000
'hhiiiiiiiiii00000800\r\n'   g.tell()== 8192
size of fileA after truncating :  8192

.

Итак, AKX и Fenisko правы при вызове буфера (однако они не проверяли эту гипотезу больше, чем я), поскольку открытие файла в режиме 'a' не влияет на действие усечения ) . Я думаю, что это то, что написано в верхнем регистре в следующей выдержке из документа:

file.truncate ([size]) Обрезать размер файла. Если дополнительный размер аргумент присутствует, файл усеченный до (не более) этого размера. размер по умолчанию соответствует текущей позиции. СОВРЕМЕННОЕ ПОЛОЖЕНИЕ ФАЙЛА НЕ ИЗМЕНИТЬ

http://docs.python.org/library/stdtypes.html#file.truncate

До сих пор я никогда не понимал это предложение.

.

Как указывает AKX, размер буфера составляет 8192 .... для первого чтения.

Но для следующих чтений буфер явно 10240 символов:

print '\n=================== 2 ====================\n'

from os.path import getsize

# length of ecr is 90 :
ecr = 10*'a' + 10*'b' + 10*'c' + 10*'d' + 10*'e' +\
      10*'f' + 10*'g' + 10*'h' + 10*'i'


# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # length of each written line is 100
        # file's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
# truncating file at uncontroled position
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00008100' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # in the file if there wasn't a reading buffer
            g.truncate()
print 'size of fileA after truncating : ',getsize('fileA.txt')

# -----------

print
# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # length of each written line is 100
        # file's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
# truncating file at uncontroled position
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00008200' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # in the file if there wasn't a reading buffer
            g.truncate()
print 'size of fileA after truncating : ',getsize('fileA.txt')

# -----------

print
# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # length of each written line is 100
        # file's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
# truncating file at uncontroled position
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00018400' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # in the file if there wasn't a reading buffer
            g.truncate()
print 'size of fileA after truncating : ',getsize('fileA.txt')

# -----------

print
# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # length of each written line is 100
        # file's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
# truncating file at uncontroled position
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00018500' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # in the file if there wasn't a reading buffer
            g.truncate()
print 'size of fileA after truncating : ',getsize('fileA.txt')

результат

=================== 2 ====================

size of fileA before truncating :  53000
'hhiiiiiiiiii00008100\r\n'   g.tell()== 8192
size of fileA after truncating :  8192

size of fileA before truncating :  53000
'hhiiiiiiiiii00008200\r\n'   g.tell()== 18432
size of fileA after truncating :  18432

size of fileA before truncating :  53000
'hhiiiiiiiiii00018400\r\n'   g.tell()== 18432
size of fileA after truncating :  18432

size of fileA before truncating :  53000
'hhiiiiiiiiii00018500\r\n'   g.tell()== 28672
size of fileA after truncating :  28672

.

Кстати, truncate () не закрывает файл:

print '\n=================== 3 ====================\n'

from os.path import getsize

# length of ecr is 90 :
ecr = 10*'a' + 10*'b' + 10*'c' + 10*'d' + 10*'e' +\
      10*'f' + 10*'g' + 10*'h' + 10*'i'


# creation of a file whose length exceeds the reading buffer's size
with open('fileA.txt','wb') as f:
    for i in xrange(100,53001,100): # 530 turns of iteration
        f.write(ecr + str(i).zfill(8) + '\r\n')
        # length of each written line is 100
        # file's length will be 53000


print 'size of fileA before truncating : ',getsize('fileA.txt')
with open('fileA.txt','a+b') as g:
    for line in g:
        if '00000200' in line:
            print repr(line[78:]),'  g.tell()==',g.tell()
            # at this point, 800 characters should have been read
            # if there wasn't a buffer
            g.truncate()
    g.seek(6000,0)
    k = 0
    for li in g:
        k+=1
        print 'k==',k,'   ',repr(li[-32:])
        if k==7:
            break
print 'size of fileA after truncating : ',getsize('fileA.txt')

результат

=================== 3 ====================

size of fileA before truncating :  53000
'hhiiiiiiiiii00000200\r\n'   g.tell()== 8192
k== 1     'gghhhhhhhhhhiiiiiiiiii00006100\r\n'
k== 2     'gghhhhhhhhhhiiiiiiiiii00006200\r\n'
k== 3     'gghhhhhhhhhhiiiiiiiiii00006300\r\n'
k== 4     'gghhhhhhhhhhiiiiiiiiii00006400\r\n'
k== 5     'gghhhhhhhhhhiiiiiiiiii00006500\r\n'
k== 6     'gghhhhhhhhhhiiiiiiiiii00006600\r\n'
k== 7     'gghhhhhhhhhhiiiiiiiiii00006700\r\n'
size of fileA after truncating :  8192

Но если инструкция для записи помещается сразу после truncate () , поведение программы становится непоследовательным. Попробуй.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...