Загрузка текстовых файлов с помощью Python и ftplib.FTP из z/os
Вопрос
Я пытаюсь автоматизировать загрузку некоторых текстовых файлов из PDS z/os, используя Python и ftplib.
Поскольку хост-файлы имеют формат EBCDIC, я не могу просто использовать FTP.retrbinary().
FTP.retrlines() при использовании с open(file,w).writelines в качестве обратного вызова, конечно, не предоставляет EOL.
Итак, для начала я придумал этот фрагмент кода, который «мне кажется нормальным», но, поскольку я относительный новичок в Python, может ли кто-нибудь предложить лучший подход?Очевидно, чтобы не усложнять этот вопрос, это не последний вопрос.
Большое спасибо.
#!python.exe
from ftplib import FTP
class xfile (file):
def writelineswitheol(self, sequence):
for s in sequence:
self.write(s+"\r\n")
sess = FTP("zos.server.to.be", "myid", "mypassword")
sess.sendcmd("site sbd=(IBM-1047,ISO8859-1)")
sess.cwd("'FOO.BAR.PDS'")
a = sess.nlst("RTB*")
for i in a:
sess.retrlines("RETR "+i, xfile(i, 'w').writelineswitheol)
sess.quit()
Обновлять:Python 3.0, платформа MingW под Windows XP.
PDS z/os имеют фиксированную структуру записей, а не используют окончания строк в качестве разделителей записей.Однако FTP-сервер z/os при передаче в текстовом режиме предоставляет окончания записей, которые retrlines() удаляет.
Закрытие обновления:
Вот мое переработанное решение, которое станет основой для постоянной разработки (например, удаление встроенных паролей):
import ftplib
import os
from sys import exc_info
sess = ftplib.FTP("undisclosed.server.com", "userid", "password")
sess.sendcmd("site sbd=(IBM-1047,ISO8859-1)")
for dir in ["ASM", "ASML", "ASMM", "C", "CPP", "DLLA", "DLLC", "DLMC", "GEN", "HDR", "MAC"]:
sess.cwd("'ZLTALM.PREP.%s'" % dir)
try:
filelist = sess.nlst()
except ftplib.error_perm as x:
if (x.args[0][:3] != '550'):
raise
else:
try:
os.mkdir(dir)
except:
continue
for hostfile in filelist:
lines = []
sess.retrlines("RETR "+hostfile, lines.append)
pcfile = open("%s/%s"% (dir,hostfile), 'w')
for line in lines:
pcfile.write(line+"\n")
pcfile.close()
print ("Done: " + dir)
sess.quit()
Я благодарю Джона и Винея.
Решение
Только что наткнулся на этот вопрос, когда пытался понять, как рекурсивно загружать наборы данных из z/OS.Я уже много лет использую простой скрипт Python для загрузки файлов ebcdic с мэйнфрейма.Фактически он просто делает это:
def writeline(line):
file.write(line + "\n")
file = open(filename, "w")
ftp.retrlines("retr " + filename, writeline)
Другие советы
У вас должна быть возможность загрузить файл в двоичном виде (используя retrbinary
) и используйте codecs
модуль для преобразования из EBCDIC в любую выходную кодировку, которую вы хотите.Вам необходимо знать конкретную кодовую страницу EBCDIC, используемую в системе z/OS (например,CP500).Если файлы небольшие, вы даже можете сделать что-то вроде (для преобразования в UTF-8):
file = open(ebcdic_filename, "rb")
data = file.read()
converted = data.decode("cp500").encode("utf8")
file = open(utf8_filename, "wb")
file.write(converted)
file.close()
Обновлять: Если вам нужно использовать retrlines
чтобы получить строки и ваши строки возвращаются в правильной кодировке, ваш подход не будет работать, поскольку обратный вызов вызывается один раз для каждой строки.Итак, в обратном вызове sequence
будет строка, и ваш цикл for будет записывать отдельные символы в строке на вывод, каждый на своей линии.Итак, вы, вероятно, хотите сделать self.write(sequence + "\r\n")
а не for
петля.Создание подкласса по-прежнему кажется не совсем правильным file
просто добавьте этот служебный метод - вероятно, он должен находиться в другом классе вашего bells-and-whistles
версия.
Ваш метод writelineswitheol добавляет « » вместо « », а затем записывает результат в файл, открытый в текстовом режиме.Результатом, независимо от того, на какой платформе вы работаете, будет нежелательный символ ' '.Просто добавьте ' ', и вы получите соответствующее окончание строки.
Правильную обработку ошибок не следует относить к версии «наворотов».Вам следует настроить обратный вызов так, чтобы ваш файл open() находился в режиме try/Exception и сохранял ссылку на дескриптор выходного файла, ваш вызов записи находился в режиме try/Exception, и у вас был метод callback_obj.close(), который вы используете, когда retrlines() возвращается к явному file_handle.close() (при попытке/исключении) - таким образом вы получаете явную обработку ошибок, например.сообщения «невозможно (открыть|записать в|закрыть) файл X, потому что Y» И вам не придется думать о том, когда ваши файлы будут неявно закрыты и не рискуете ли вы исчерпать дескрипторы файлов.
Python 3.x ftplib.FTP.retrlines() должен предоставлять вам объекты str, которые по сути являются строками Unicode, и вам нужно будет закодировать их перед записью - если только кодировкой по умолчанию не является latin1, что было бы довольно необычно для Windows. коробка.У вас должны быть тестовые файлы с (1) всеми возможными 256 байтами (2) всеми байтами, которые действительны в ожидаемой кодовой странице EBCDIC.
[несколько "санитарных" замечаний]
Вам следует рассмотреть возможность обновления Python с версии 3.0 (выпуск «доказательства концепции») до версии 3.1.
Чтобы облегчить лучшее понимание вашего кода, используйте «i» в качестве идентификатора только в качестве индекса последовательности и только в том случае, если вы безнадежно приобрели привычку из FORTRAN 3 или более десятилетий назад :-)
Две из обнаруженных до сих пор проблем (добавление признака конца строки к каждому символу, неправильный признак завершения строки) должны были проявиться при первом тестировании.