6

Python と ftplib を使用して、az/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()

ジョンとビナイの両方に感謝します

4

5 に答える 5

5

z/OS からデータセットを再帰的にダウンロードする方法を見つけようとしていたときに、この質問に出くわしました。メインフレームから ebcdic ファイルをダウンロードするために、私は何年も前からシンプルな Python スクリプトを使用してきました。効果的にこれを行うだけです:

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

file = open(filename, "w")
ftp.retrlines("retr " + filename, writeline)
于 2011-11-01T10:13:45.453 に答える
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あり、行が正しいエンコーディングで戻ってくる場合、コールバックは行ごとに 1 回呼び出されるため、このアプローチは機能しません。したがって、コールバックでsequenceは行になり、 for ループは行内の個々の文字をそれぞれ独自の行に出力に書き込みます。したがって、おそらくループではself.write(sequence + "\r\n")なくやりたいと思うでしょう。forただし、このユーティリティ メソッドを追加するためだけにサブクラス化するのは特に適切ではありません。fileおそらく、バージョン内の別のクラスにする必要がありますbells-and-whistles

于 2009-07-26T15:39:56.220 に答える
1

writelineswitheolメソッドは、「\n」の代わりに「\r \ n」を追加し、その結果をテキストモードで開いたファイルに書き込みます。実行しているプラ​​ットフォームに関係なく、その影響は不要な「\r」になります。'\ n'を追加するだけで、適切な行末が表示されます。

適切なエラー処理は、「ベルとホイッスル」バージョンに追いやられるべきではありません。ファイルopen()がtry / exceptionにあり、出力ファイルハンドルへの参照を保持し、書き込み呼び出しがtry / exceptionにあり、callback_obj.close()メソッドがあるようにコールバックを設定する必要があります。 retrlines()が明示的にfile_handle.close()に戻るときに使用します(試行/例外で)-このようにすると、明示的なエラー処理が発生します。ファイルが暗黙的に閉じられる時期や、ファイルハンドルが不足するリスクがあるかどうかを考える必要がなくなります。

Python 3.x ftplib.FTP.retrlines()は、実際にはUnicode文字列であるstrオブジェクトを提供する必要があり、書き込む前にエンコードする必要があります。ただし、デフォルトのエンコードがlatin1である場合を除きます。これは、Windowsではかなり珍しいことです。箱。(1)すべての可能な256バイト(2)予想されるEBCDICコードページで有効なすべてのバイトを含むテストファイルが必要です。

[いくつかの「衛生」発言]

  1. Pythonを3.0(「概念実証」リリース)から3.1にアップグレードすることを検討する必要があります。

  2. コードをよりよく理解するために、「i」を識別子として使用するのは、シーケンスインデックスとしてのみであり、3年以上前にFORTRANから習慣を取り返しのつかないほど取得した場合に限ります:-)

  3. これまでに発見された2つの問題(各文字にラインターミネータを追加する、間違ったラインターミネータ)は、最初にテストしたときに現れたはずです。

于 2009-07-27T00:54:17.900 に答える