Загрузка текстовых файлов с помощью Python и ftplib.FTP из z/os

StackOverflow https://stackoverflow.com/questions/1184844

  •  19-09-2019
  •  | 
  •  

Вопрос

Я пытаюсь автоматизировать загрузку некоторых текстовых файлов из 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.

[несколько "санитарных" замечаний]

  1. Вам следует рассмотреть возможность обновления Python с версии 3.0 (выпуск «доказательства концепции») до версии 3.1.

  2. Чтобы облегчить лучшее понимание вашего кода, используйте «i» в качестве идентификатора только в качестве индекса последовательности и только в том случае, если вы безнадежно приобрели привычку из FORTRAN 3 или более десятилетий назад :-)

  3. Две из обнаруженных до сих пор проблем (добавление признака конца строки к каждому символу, неправильный признак завершения строки) должны были проявиться при первом тестировании.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top