【问题标题】:Downloading text files with Python and ftplib.FTP from z/os使用 Python 和 ftplib.FTP 从 z/os 下载文本文件
【发布时间】:2010-11-14 03:58:32
【问题描述】:

我正在尝试使用 Python 和 ftplib 从 z/os PDS 自动下载一些文本文件。

由于主机文件是 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,平台为 Windows XP 下的 MingW。

z/os PDS 具有固定的记录结构,而不是依赖行结尾作为记录分隔符。但是,z/os FTP 服务器在以文本模式传输时,会提供记录结尾,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()

感谢约翰和维奈

【问题讨论】:

  • 请编辑您的问题以提及和描述 PDS 文件。 “一些文本文件”相当不够。
  • 另外请说明什么平台,什么版本的 Python,以及为什么你的 writelineswitheol 方法会附加 '\r\n' 而不是 '\n'。并请说明您是否实际运行过此程序并检查输出以确保它具有适合您平台的正确线路终止。
  • 完成。我在 corp.firewall 外面的家里做一些周末编码,所以我只会在本周晚些时候测试这个想法。

标签: python ftp mainframe zos


【解决方案1】:

在我试图弄清楚如何从 z/OS 递归下载数据集时遇到了这个问题。多年来,我一直在使用简单的 python 脚本从大型机下载 ebcdic 文件。它实际上就是这样做的:

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

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

【讨论】:

  • file is not defined
【解决方案2】:

您应该能够将文件下载为二进制文件(使用retrbinary)并使用codecs 模块从EBCDIC 转换为您想要的任何输出编码。您应该知道 z/OS 系统上使用的特定 EBCDIC 代码页(例如 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 版本中位于不同的类中。

【讨论】:

  • 谢谢,Vinay,这是一个有趣的想法,但我如何插入换行符? (这些是传统的 zos PDS,不是 OpenEdition 文件)
  • 如果不使用 EBCDIC 换行符,主机系统上的行如何终止?
  • 主机文件系统是基于记录的。它要么是固定长度,在这种情况下所有记录都具有相同的长度,要么是可变长度,其中长度存储在每个记录开头的描述符字段中。 FTP.retrlines() 正确提取记录,但(我认为是正确的)不提供换行符。
  • @Vinay.Update:哎呀,是的,我明白了。本周晚些时候,当我回到大型机时,我会尝试一些想法,然后回复。
【解决方案3】:

您的 writelineswitheol 方法附加 '\r\n' 而不是 '\n',然后将结果写入以文本模式打开的文件。无论您在什么平台上运行,效果都将是一个不需要的 '\r'。只需附加 '\n' 即可获得适当的行尾。

不应将正确的错误处理归结为“花里胡哨”的版本。您应该设置回调,以便您的文件 open() 在 try/except 中并保留对输出文件句柄的引用,您的 write 调用在 try/except 中,并且您有一个 callback_obj.close() 方法您在 retrlines() 返回显式 file_handle.close() 时使用(在 try/except 中) - 这样您就可以得到显式错误处理,例如消息“不能(打开|写入|关闭)文件 X,因为 Y”并且您不必考虑文件何时将被隐式关闭以及是否有文件句柄用完的风险。

Python 3.x ftplib.FTP.retrlines() 应该为您提供实际上是 Unicode 字符串的 str 对象,并且您需要在编写它们之前对其进行编码——除非默认编码是 latin1,这将是相当不寻常的对于 Windows 框。您应该拥有包含 (1) 所有可能的 256 字节 (2) 在预期 EBCDIC 代码页中有效的所有字节的测试文件。

[一些“卫生”言论]

  1. 您应该考虑将 Python 从 3.0(“概念证明”版本)升级到 3.1。

  2. 为了便于更好地理解您的代码,仅当您从 FORTRAN 3 或更多年前不可挽回地养成习惯时,才使用“i”作为标识符作为序列索引 :-)

    李>
  3. 到目前为止发现的两个问题(将行终止符附加到每个字符,错误的行终止符)会在您第一次测试时出现。

【讨论】:

  • 约翰,谢谢。请放心,我已经接受了您的公正批评。
【解决方案4】:

使用ftplib的retrlines从z/os下载文件,每一行没有'\n'。

它不同于 windows ftp 命令'get xxx'。

我们可以在 ftplib.py 中将函数 'retrlines' 重写为 'retrlines_zos'。

只需复制整个 retrlines 代码,并将“回调”行更改为:

...

回调(line + "\n")

...

我测试过,效果很好。

【讨论】:

  • 回调?什么回调?代码,请。
【解决方案5】:

你想要一个 lambda 函数和一个回调。像这样:

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

ftpcommand = "RETR {}{}{}".format("'",zOsFile,"'")  
filename = "newfilename"
with open( filename, 'w' ) as file :
     callback_lambda = lambda x: writeLineCallback(x,file)
     ftp.retrlines(ftpcommand, callback_lambda)

这将下载文件'zOsFile'并将其写入'newfilename'

【讨论】:

    猜你喜欢
    • 2023-02-23
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 2011-05-29
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多