Domanda

Sto cercando di automatizzare il download di alcuni file di testo da un z / OS PDS, utilizzando Python e ftplib.

Dal momento che i file host sono EBCDIC, non posso semplicemente usare FTP.retrbinary ().

FTP.retrlines (), quando viene utilizzato con aperta (file, w) .writelines come il suo richiamo, non, ovviamente, fornire EOLs.

Quindi, per cominciare, mi è venuta in mente questo pezzo di codice che "sembra OK a me", ma come io sono un parente niubbo Python, qualcuno può suggerire un approccio migliore? Ovviamente, per mantenere questa semplice domanda, questa non è la finale, cosa campane-e-fischi.

Molte grazie.

#!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()

Aggiornamento: Python 3.0, la piattaforma è MingW sotto Windows XP

.

z / os PDS hanno una struttura di registrazione fissa, piuttosto che basarsi su terminazioni di linea come separatori record. Tuttavia, il server FTP z / os, caso di trasmissione in modalità testo, fornisce le terminazioni di registrazione, che retrlines () mette nudo.

Aggiornamento di chiusura:

Ecco la mia soluzione rivisto, che sarà la base per lo sviluppo continuo (la rimozione delle password built-in, per esempio):

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()

I miei ringraziamenti a John e Vinay

È stato utile?

Soluzione

Proprio imbattuto in questa domanda come stavo cercando di capire come scaricare ricorsivamente set di dati da z / OS. Sto usando uno script python semplice ormai da anni per scaricare file EBCDIC dal mainframe. E 'effettivamente fa proprio questo:

def writeline(line):
    file.write(line + "\n")

file = open(filename, "w")
ftp.retrlines("retr " + filename, writeline)

Altri suggerimenti

Si dovrebbe essere in grado di scaricare il file come un binario (utilizzando retrbinary) e utilizzare il modulo codecs convertire da EBCDIC su qualunque uscita codifica che si desidera. Si deve sapere la pagina specifica di codice EBCDIC in uso sul sistema z / OS (ad esempio CP500). Se i file sono di piccole dimensioni, si potrebbe anche fare qualcosa di simile (per una conversione a 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()

Aggiornamento: Se avete bisogno di utilizzare retrlines per ottenere le linee e le linee stanno tornando nella codifica corretta, il tuo approccio non funziona, perché il callback viene chiamato una volta per ogni linea. Così nel callback, sequence sarà la linea, e il vostro ciclo for scriverà singoli caratteri in linea con l'uscita, ognuno su una riga . Quindi probabilmente si vuole fare self.write(sequence + "\r\n") piuttosto che il ciclo for. E ancora doesn' sentire soprattutto diritto di sottoclasse file solo aggiungere questo metodo di utilità, anche se - probabilmente ha bisogno di essere in una classe diversa nella versione bells-and-whistles

.

Il tuo metodo writelineswitheol aggiunge '\ r \ n' invece di '\ n' e quindi scrive il risultato a un file aperto in modalità testo. L'effetto, non importa quale piattaforma si sta lavorando su, sarà un indesiderato '\ r'. Basta aggiungere '\ n' e si otterrà la riga appropriata fine.

Una corretta gestione degli errori non dovrebbe essere relegata a una versione "campane e fischietti". Si dovrebbe impostare il callback in modo che il file open () si trova in un try / except e mantiene un riferimento al manico file di output, la chiamata di scrittura è in un try / tranne, e si dispone di un metodo callback_obj.close (), che si usa quando retrlines () restituisce in modo esplicito file_handle.close () (in un blocco try / tranne) - in questo modo si ottiene explict gestione degli errori per esempio messaggi "non può (aperto | scrittura a | close) file X perché Y". E si salva dover pensare a quando i file stanno per essere implicitamente chiuso e se si rischia di rimanere a corto di handle di file

ftplib.FTP.retrlines Python 3.x () dovrebbe darvi gli oggetti str che sono in stringhe Unicode effetto, e sarà necessario per codificare loro prima di scrivere - a meno che la codifica di default è latin1 che sarebbe piuttosto insolito per una scatola di Windows. Si dovrebbe avere file di test con (1) tutte le possibili 256 byte (2) tutti i byte che sono validi nella tabella codici EBCDIC previsto.

[a qualche osservazione "servizi igienico-sanitari"]

  1. Si dovrebbe prendere in considerazione l'aggiornamento del Python da 3.0 (un "proof of concept" release) a 3,1.

  2. Per facilitare una migliore comprensione del codice, usare "i" come identificatore solo come indice di sequenza e solo se si è irrimediabilmente preso l'abitudine da FORTRAN 3 o più decenni fa: -)

  3. Due dei problemi scoperti finora (aggiungendo terminazione di linea per ogni carattere, linea sbagliata terminatore) avrebbero dimostrato la prima volta che l'avete provato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top