Почему «\x01\x1A» (начальные символы заголовка и подстановочные управляющие символы) в строке текстового файла преждевременно останавливают цикл for?

Я использую Python 2.7.15, Windows 7

Контекст

Я написал сценарий для чтения и разметки каждой строки файла журнала FileZilla (спецификации здесь) для IP-адрес хоста, который инициировал подключение к серверу FileZilla. У меня возникли проблемы с разбором поля log text, следующего за символом >. Сценарий, который я написал, использует:

    with open('fz.log','r') as rh:
       for lineno, line in rh: 
          pass

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

Моя цель — извлечь IP-адреса (что я могу сделать с помощью re.search()), но прежде чем это произойдет, я должен удалить эти управляющие символы. Я делаю это, создавая копию файла журнала, в которой удаляются строки, содержащие эти управляющие символы. Вероятно, есть лучший способ, но мне больше любопытно, почему цикл for просто останавливается после встречи с управляющими символами.

Воспроизведение проблемы

Я воспроизвел проблему с этим кодом:

if __name__ == '__main__':
    fn = 'writetest.txt'
    fn2 = 'writetest_NoControlChars.txt'

    # Create the problematic textfile
    with open(fn, 'w') as wh: 
        wh.write("This line comes first!\n");
        wh.write("Blah\x01\x1A\n"); # Write Start-of-Header and Subsitute unicode character to line
        wh.write("This comes after!")

    # Try to read the file above, removing the SOH/SUB characters if encountered
    with open(fn, 'r') as rh:
        with open(fn2, 'w') as wh:
            for lineno, line in enumerate(rh):
                sline = line.translate(None,'\x01\x1A')
                wh.write(sline)
                print "Line #{}: {}".format(lineno, sline)
    print "Program executed."

Вывод

Приведенный выше код создает 2 выходных файла и выводит в окне консоли следующее:

Line #0: This line comes first!

Line #1: Blah
Program executed.

Я выполнил пошаговую отладку кода в Eclipse и сразу после выполнения

for lineno, line in enumerate(rh): 

заявление, rh, дескриптор для этого открытого файла был закрыт. Я ожидал, что он переместится на третью строку, выведет This comes after! на консоль и запишет в writetest_NoControlChars.txt, но ничего не произошло. Вместо этого выполнение подскочило до print "Program executed". Изображение значений локальной переменной в консоли отладки


person Minh T.    schedule 02.11.2018    source источник
comment
Вы должны открыть этот файл в двоичном режиме, если знаете, что он содержит нетекстовые данные: open(fn, 'rb')   -  person mvp    schedule 02.11.2018
comment
Изменение with open(fn, 'r') as rh: на with open(fn, 'rb') as rh:, как вы предложили, сработало. Спасибо (буду рад принять ваш ответ, если вы опубликуете его как таковой)!   -  person Minh T.    schedule 02.11.2018
comment
Я копнул немного глубже, и в документации для open() говорится об открытии файл в двоичном режиме возвращает содержимое файла в виде байтов без декодирования. По умолчанию open() работает в режиме 'Text I/O', поэтому он не работал. Я просмотрел API для модуля ввода-вывода, но не смог Не могу найти ничего, что объясняло бы, почему файловый дескриптор закрывается в текстовом режиме при встрече с этими символами. Я что-то пропустил? Как я могу копнуть глубже для объяснения?   -  person Minh T.    schedule 02.11.2018
comment
Какую операционную систему и версию Python вы используете? На моем Mac с Python 2.7.11 ваш код выше сообщает «Это будет после!» в подходящее время.   -  person Matthias Fripp    schedule 02.11.2018
comment
0x1A в системах DOS и Windows является кодом конца текста Ctrl-Z для ввода-вывода в текстовом режиме.   -  person Jongware    schedule 02.11.2018
comment
.. однако я не могу найти упоминания об этом в исходном коде _iobase и _textiobase. Таким образом, в конечном счете, это может использовать стандартную библиотеку C, которая также делает то же самое для Windows.   -  person Jongware    schedule 02.11.2018
comment
@ usr2564301 Это объясняет, почему, если я написал и прочитал только \x01, а не \x01\x1A, программа работает нормально!   -  person Minh T.    schedule 03.11.2018


Ответы (1)


Вы должны открыть этот файл в двоичном режиме, если знаете, что он содержит нетекстовые данные: open(fn, 'rb')

person mvp    schedule 02.11.2018
comment
Что решает, что является нетекстовыми данными? У вас есть ссылка на это в официальной документации? - person Jongware; 03.11.2018
comment
Я предполагаю, что любой поток байтов, которому нельзя назначить какую-либо известную текстовую кодировку (utf-8, latin1, windows-1251 и т. д.), следует считать двоичным. Открытие файла в двоичном режиме отключает любой автоматический анализ/обработку, который происходит для текстовых файлов. - person mvp; 03.11.2018