2009-07-26 28 views
3

我試圖使用Python和ftplib自動從z/os PDS下載一些文本文件。使用Python和ftplib.FTP從z/os下載文本文件

由於主機文件是EBCDIC,我不能簡單地使用FTP.retrbinary()。

當使用open(file,w).writelines作爲其回調函數時,FTP.retrlines()當然不會提供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,平臺是MingW在Windows XP下。

z/os PDSs有一個固定的記錄結構,而不是依靠行結尾作爲記錄分隔符。但是,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() 

我要感謝約翰和兩個Vinay

+0

請編輯您的問題提及並描述PDS文件。 「一些文本文件」是相當不足的。 – 2009-07-26 15:58:06

+0

另請說明什麼平臺,什麼版本的Python,以及爲什麼你的writelineswitheol方法附加'\ r \ n'而不是'\ n'。並且請說明您是否真的執行了此操作並檢查了輸出以確保其平臺具有正確的線路終端。 – 2009-07-26 16:32:21

+0

完成。我在corp.firewall外面的家裏做了一些週末的編程工作,所以我只會在本週晚些時候測試這個想法。 – 2009-07-26 16:49:29

回答

3

剛剛遇到這個問題,因爲我試圖弄清楚如何遞歸地從z/OS下載數據集。我一直在使用一個簡單的Python腳本來從大型機上下載ebcdic文件。它有效地只是這樣做:

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

file = open(filename, "w") 
ftp.retrlines("retr " + filename, writeline) 
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版本的不同類中。

1

您的writelineswitheol方法會附加'\ r \ n'而不是'\ n',然後將結果寫入以文本模式打開的文件。無論你在哪個平臺上運行,這個效果都會是一個不想要的'\ r'。只需追加'\ n',您將得到相應的行結束。

正確的錯誤處理不應該降級爲「花裏胡哨的」版本。你應該設置你的回調,以便你的文件open()處於try/except狀態,並且保留對輸出文件句柄的引用,你的write調用處於try/except狀態,並且你有一個callback_obj.close你使用時retrlines()顯式返回file_handle.close()(在一個嘗試/除外) - 這樣你得到的顯式錯誤處理例如消息「不能(打開|寫入|關閉)文件X,因爲Y」並且您不必考慮什麼時候文件將隱式關閉以及您是否有可能耗盡文件句柄。

Python 3.x ftplib.FTP.retrlines()應該爲您提供實際上是Unicode字符串的str對象,並且您在編寫它們之前需要對它們進行編碼 - 除非默認編碼是latin1,不常見的Windows盒子。您應該有(1)所有可能的256字節的測試文件(2)在預期的EBCDIC代碼頁中有效的所有字節。

[幾個 「衛生」 的言論]

  1. 你應該考慮從3.0升級你的Python(一種 「概念證明」 版本)3.1。

  2. 爲了便於更好地理解你的代碼,用「我」作爲標識符僅作爲一個序列索引,而且你只不可挽回從FORTRAN 3個或更多年前:-)

  3. 的兩個所獲取的習慣到目前爲止發現的問題(在每個字符後附加行結束符,錯誤的行結束符)會在您首次測試它時顯示出來。