Python не может открыть 11gb csv в режиме r +, но открывается в режиме r

У меня возникли проблемы с некоторым кодом, который проходит через связку .csvs и удаляет финальную строку, если в ней ничего нет (т.е. файлы, заканчивающиеся символом \n newline)

Мой код работает успешно во всех файлах, кроме одного, который является самым большим файлом в каталоге на 11gb. Второй по величине файл - 4.5gb.

Линия, с которой он выходит, просто:

with open(path_str,"r+") as my_file:

и я получаю следующее сообщение:

IOError: [Errno 22] invalid mode ('r+') or filename: 'F:\\Shapefiles\\ab_premium\\processed_csvs\\a.csv'

path_str Я создаю с помощью os.file.join, чтобы избежать ошибок, и я попытался переименовать файл в a.csv, чтобы убедиться, что с именем файла не было ничего странного. Это не имело значения.

Еще более странно, файл счастлив открыть в режиме r. То есть следующий код работает нормально:

with open(path_str,"r") as my_file:

Я пробовал перемещаться по файлу в режиме чтения, и он счастлив читать символы в начале, в конце и в середине файла.

Кто-нибудь знает какие-либо ограничения на размер файла, с которым может справиться Python, или почему я могу получить эту ошибку? Я нахожусь на Windows 7 64 бит и имею 16 гб оперативной памяти.

Ответ 1

По умолчанию стек ввода/вывода в Python 2 накладывается поверх потоков CRT FILE. В Windows они построены поверх API эмуляции POSIX, который использует файловые дескрипторы (которые, в свою очередь, накладываются поверх пользовательского режима Windows API, который накладывается поверх системы ввода-вывода в режиме ядра, которая сама по себе является глубокоуровневой системой на основе пакетов запросов ввода-вывода, аппаратное обеспечение там где-то там...). В POSIX-слое, открывая файл с режимом _O_RDWR | _O_TEXT (как в "r +" ), требуется поиск конца файла для удаления CTRL + Z, если он присутствует. Здесь приведена цитата из документации CRT fopen:

Открыть в текстовом (переведенном) режиме. В этом режиме CTRL + Z интерпретируется как символ конца файла на входе. В файлах, открытых для чтения/записи с "a +", fopen проверяет CTRL + Z в конце файла и удаляет его, если это возможно. Это делается потому, что использование fseek и ftell для перемещение внутри файла, заканчивающегося CTRL + Z, может привести к тому, что fseek будет вести себя неправильно рядом с концом файла.

Проблема в том, что указанная выше проверка вызывает 32-битный _lseek (помните, что sizeof long - 4 байта на 64-разрядная Windows, в отличие от большинства других 64-разрядных платформ) вместо _lseeki64. Очевидно, это не удается для 11-гигабайтного файла. В частности, SetFilePointer терпит неудачу, потому что он вызывается с NULL значением для lpDistanceToMoveHigh. Здесь возвращаемое значение и LastErrorValue для последнего вызова:

0:000> kc 2
Call Site
KERNELBASE!SetFilePointer
MSVCR90!lseek_nolock

0:000> r rax                       
rax=00000000ffffffff

0:000> dt _TEB @$teb LastErrorValue
ntdll!_TEB
   +0x068 LastErrorValue : 0x57

Код ошибки 0x57 ERROR_INVALID_PARAMETER. Это означает, что lpDistanceToMoveHigh является NULL при попытке поиска с конца большого файла.

Чтобы обойти эту проблему с потоками CRT FILE, я рекомендую открыть файл, используя io.open. Это резервная реализация стека ввода-вывода Python 3. Он всегда открывает файлы в сыром двоичном режиме (_O_BINARY), и он реализует свои собственные слои буферизации и текстового режима поверх исходного слоя.

>>> import io                    
>>> f = io.open('a.csv', 'r+')
>>> f     
<_io.TextIOWrapper name='a.csv' encoding='cp1252'>
>>> f.buffer   
<_io.BufferedRandom name='a.csv'>
>>> f.buffer.raw
<_io.FileIO name='a.csv' mode='rb+'>
>>> f.seek(0, os.SEEK_END)
11811160064L