我正在尝试使用Python和ftplib自动从z / os PDS下载一些文本文件。
由于主机文件是EBCDIC,我不能简单地使用FTP.retrbinary()。
FTP.retrlines(),当与open(file,w).writelines一起用作回调时,当然不会提供EOL。
所以,对于初学者来说,我已经提出了这段“看起来对我好”的代码,但是因为我是一个相对的Python noob,有人能提出更好的方法吗?显然,为了保持这个问题的简单,这不是最后的,钟声和口哨的事情。
非常感谢。
#!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服务器在文本模式下传输时,会提供记录结尾,其后退()剥离。
结束更新:
这是我修改后的解决方案,它将成为持续开发的基础(例如删除内置密码):
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()
我要感谢John和Vinay
答案 0 :(得分:4)
刚刚遇到这个问题,因为我试图弄清楚如何从z / OS递归下载数据集。多年来我一直在使用一个简单的python脚本从大型机下载ebcdic文件。它实际上就是这样做的:
def writeline(line):
file.write(line + "\n")
file = open(filename, "w")
ftp.retrlines("retr " + filename, writeline)
答案 1 :(得分:3)
您应该能够将文件作为二进制文件下载(使用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
版本的不同类中。
答案 2 :(得分:1)
您的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代码页中有效的字节。
[一些“卫生”言论]
您应该考虑将Python从3.0(“概念证明”版本)升级到3.1。
为了便于更好地理解您的代码,只使用“i”作为标识符作为序列索引,并且只有在您不可挽回地从FORTRAN 3或更多年前获得习惯时: - )
到目前为止发现的两个问题(在每个字符附加行终止符,错误的行终止符)会在您第一次测试时出现。
答案 3 :(得分:0)
使用ftplib的retrlines从z / os下载文件,每行都没有'\ n'。
与Windows ftp命令“获取xxx”不同。
我们可以在ftplib.py中将函数'retrlines'重写为'retrlines_zos'。
只需复制整个retrlines代码,然后将“ callback”行修改为:
...
回调(行+“ \ n”)
...
我进行了测试,并且有效。