ฉันจะสร้าง zip / tgz ใน Linux ได้อย่างไรว่า Windows มีชื่อไฟล์ที่ถูกต้อง


26

ปัจจุบันทำการtar -zcf arch.tgz files/*เข้ารหัสชื่อไฟล์ใน UTF ดังนั้นผู้ใช้ Windows จะเห็นตัวละครทุกตัวที่อยู่ในชื่อไฟล์ที่ไม่ใช่ภาษาอังกฤษและไม่สามารถทำอะไรกับมันได้

zip -qq -r arch.zip files/* มีพฤติกรรมเดียวกัน

ฉันจะสร้างไฟล์ zip / tgz ได้อย่างไรเมื่อผู้ใช้ Windows แตกไฟล์มันจะมีการเข้ารหัสชื่อไฟล์ทั้งหมดอย่างถูกต้อง?

คำตอบ:


24

ปัจจุบัน tar เข้ารหัสไฟล์ชื่อใน UTF

จริงๆแล้ว tar ไม่ได้เข้ารหัส / ถอดรหัสชื่อไฟล์เลยเพียงแค่คัดลอกไฟล์เหล่านั้นออกจากระบบไฟล์ตามที่เป็นอยู่ หากโลแคลของคุณใช้ UTF-8 (เหมือนกับใน Linux distros ที่ทันสมัย) นั่นจะเป็น UTF-8 น่าเสียดายที่เพจรหัสระบบของกล่อง Windows ไม่เคยเป็น UTF-8 ดังนั้นชื่อต่างๆจะมีการพันกันเสมอยกเว้นในเครื่องมือต่าง ๆ เช่น WinRAR ที่อนุญาตให้เปลี่ยนชุดอักขระ

ดังนั้นจึงเป็นไปไม่ได้ที่จะสร้างไฟล์ ZIP ที่มีชื่อไฟล์ที่ไม่ใช่ ASCII ซึ่งทำงานกับ Windows รุ่นต่าง ๆ ของประเทศและการรองรับโฟลเดอร์ที่ถูกบีบอัด

เป็นข้อบกพร่องของรูปแบบ tar และ zip ที่ไม่มีข้อมูลการเข้ารหัสที่แน่นอนหรือที่ให้มาดังนั้นอักขระที่ไม่ใช่ ASCII จะไม่ใช่แบบพกพาเสมอ หากคุณต้องการรูปแบบการเก็บถาวรที่ไม่ใช่ ASCII คุณจะต้องใช้หนึ่งในรูปแบบที่ใหม่กว่าเช่น 7z หรือ rar ล่าสุด โชคไม่ดีที่สิ่งเหล่านี้ยังคงไร้ค่า ใน 7zip คุณต้องใช้-mcuสวิตช์และ rar จะไม่ใช้ UTF-8 ยกเว้นว่าตรวจพบอักขระที่ไม่ได้อยู่ในเพจรหัส

โดยทั่วไปแล้วมันเป็นเรื่องที่น่ากลัวและถ้าคุณหลีกเลี่ยงการกระจายไฟล์เก็บถาวรที่มีชื่อไฟล์ด้วยอักขระที่ไม่ใช่ ASCII คุณจะดีขึ้นมาก


เยี่ยมมากขอบคุณ! น่าเสียดายที่ผู้ใช้ส่วนใหญ่ไม่รู้อะไรเลยเกี่ยวกับ 7z และ rar เป็นกรรมสิทธิ์ของ :(
kolypto

ใช่มันเป็นปัญหา ZIP นั้นเป็นโซลูชันที่ใช้งานได้ดีที่สุดสำหรับผู้ใช้เนื่องจากระบบปฏิบัติการที่ทันสมัยทั้งหมดนั้นรองรับ UI ดั้งเดิมอย่างดี น่าเสียดายที่ปัญหาชุดอักขระไม่สามารถแก้ไขได้ในวันนี้จริง ๆ ใน ZIP (และแม้แต่ในรูปแบบไฟล์เก็บถาวรอื่น ๆ ก็ยังคงเป็นปัญหา)
bobince

25

นี่เป็นสคริปต์ Python ง่าย ๆ ที่ฉันเขียนเพื่อคลายไฟล์ tar จาก UNIX บน Windows:

import tarfile

archive_name = "archive_name.tar"

def recover(name):
    return unicode(name, 'utf-8')

tar = tarfile.open(name=archive_name, mode='r', bufsize=16*1024)
updated = []
for m in tar.getmembers():
    m.name = recover(m.name)
    updated.append(m)

tar.extractall(members=updated)
tar.close()

! น่ากลัว สคริปต์นี้ช่วยให้ฉันแปลงไฟล์ tar ที่เข้ารหัส EUC-JP ที่สร้างขึ้นบนเซิร์ฟเวอร์ Solaris เก่า
wm_eddie

ท่านช่วยชีวิตฉันไว้ ขอพระเจ้าอวยพรคุณ :)
1576772

8

ปัญหาการใช้งานใน Linux เป็นค่าเริ่มต้นtar(tar GNU) ได้รับการแก้ไข ... การเพิ่ม--format=posixพารามิเตอร์เมื่อสร้างไฟล์

ตัวอย่างเช่น:
tar --format=posix -cf

ใน Windows เพื่อดึงไฟล์ที่ผมใช้bsdtar

ในhttps://lists.gnu.org/archive/html/bug-tar/2005-02/msg00018.htmlเป็นลายลักษณ์อักษร (ตั้งแต่ปี 2005 !!):

> ฉันอ่านบางอย่างใน ChangeLog เกี่ยวกับการรองรับ UTF-8 สิ่ง
นี้หมายความว่าอะไร
> ฉันไม่พบวิธีสร้างไฟล์เก็บถาวรที่จะใช้แทนกันได้
> ระหว่างตำแหน่งที่ตั้งอื่น

เมื่อสร้างไฟล์เก็บถาวรในรูปแบบ POSIX.1-2001 (tar --format = posix หรือ --format = pax) tar จะแปลงชื่อไฟล์จากโลแคลปัจจุบันเป็น UTF-8 จากนั้นเก็บไว้ในไฟล์เก็บถาวร เมื่อทำการแยกการดำเนินการย้อนกลับจะดำเนินการ

ป.ล. แทนที่จะพิมพ์--format=posixคุณสามารถพิมพ์-H paxได้ซึ่งสั้นกว่า


5

ฉันเชื่อว่าคุณกำลังประสบปัญหากับรูปแบบ Zip คอนเทนเนอร์เอง น้ำมันดินอาจเป็นทุกข์จากปัญหาเดียวกัน

ใช้รูปแบบไฟล์เก็บถาวร7zip ( .7z) หรือ RAR ( .rar) แทน ทั้งสองพร้อมใช้งานสำหรับ Windows และ Linux p7zipซอฟต์แวร์จัดการรูปแบบทั้ง

ฉันเพิ่งทดสอบการสร้าง.7z, .rarและ.zip, .tarไฟล์ทั้งบน WinXP และ Debian 5 และ.7zและ.rarไฟล์และที่เก็บ / กู้คืนชื่อไฟล์อย่างถูกต้องในขณะที่.zipและ.tarไฟล์ไม่ ไม่สำคัญว่าจะใช้ระบบใดในการสร้างไฟล์เก็บถาวรการทดสอบ


5

ฉันมีปัญหากับการแกะกล่องtarและzipไฟล์ที่ฉันได้รับจากผู้ใช้ Windows ในขณะที่ฉันไม่ตอบคำถาม "วิธีสร้างที่เก็บถาวรซึ่งจะใช้งานได้" สคริปต์ด้านล่างช่วยในการคลายไฟล์tarและzipไฟล์อย่างถูกต้องโดยไม่คำนึงถึงระบบปฏิบัติการดั้งเดิม

คำเตือน: หนึ่งที่มีการปรับแต่งแหล่งที่เข้ารหัสด้วยตนเอง ( cp1251, cp866ในตัวอย่างด้านล่าง) ตัวเลือกบรรทัดคำสั่งอาจเป็นทางออกที่ดีในอนาคต

tar:

#!/usr/bin/env python

import tarfile
import codecs
import sys

def recover(name):
    return codecs.decode(name, 'cp1251')

for tar_filename in sys.argv[1:]:
    tar = tarfile.open(name=tar_filename, mode='r', bufsize=16*1024)
    updated = []
    for m in tar.getmembers():
        m.name = recover(m.name)
        updated.append(m)
    tar.extractall(members=updated)
    tar.close()

ไปรษณีย์:

#!/usr/bin/env python

import zipfile
import os
import codecs
import sys

def recover(name):
    return codecs.decode(name, 'cp866')

for filename in sys.argv[1:]:
    archive = zipfile.ZipFile(filename, 'r')
    infolist = archive.infolist()
    for i in infolist:
        f = recover(i.filename)
        print f
        if f.endswith("/"):
            os.makedirs(os.path.dirname(f))
        else:
            open(f, 'w').write(archive.read(i))
    archive.close()

UPD 2018-01-02 : ฉันใช้chardetแพ็คเกจเพื่อคาดเดาการเข้ารหัสที่ถูกต้องของกลุ่มข้อมูลดิบ ตอนนี้สคริปต์ทำงานนอกกรอบในคลังเก็บข้อมูลที่ไม่ดีทั้งหมดของฉันรวมถึงคลังเก็บที่ดี

สิ่งที่ควรทราบ:

  1. ชื่อไฟล์ทั้งหมดจะถูกแยกและรวมเข้าไปในสายเดียวเพื่อสร้างข้อความที่ใหญ่ขึ้นสำหรับเอ็นจิ้นการเดารหัส หมายความว่าชื่อไฟล์บางส่วนถูกเมาในวิธีที่ต่างกันซึ่งแต่ละอย่างอาจทำให้การเดาผิด
  2. พา ธ ด่วนพิเศษใช้เพื่อจัดการกับข้อความยูนิโค้ดที่ดี ( chardetไม่สามารถทำงานกับวัตถุ Unicode ปกติ)
  3. มีการเพิ่ม Doctests เพื่อทดสอบและเพื่อแสดงให้เห็นว่า Normalizer จดจำการเข้ารหัสใด ๆ ในสตริงที่สั้นพอสมควร

รุ่นสุดท้าย:

#!/usr/bin/env python2
# coding=utf-8

import zipfile
import os
import codecs
import sys

import chardet


def make_encoding_normalizer(txt):
    u'''
    Takes raw data and returns function to normalize encoding of the data.
        * `txt` is either unicode or raw bytes;
        * `chardet` library is used to guess the correct encoding.

    >>> n_unicode = make_encoding_normalizer(u"Привет!")
    >>> print n_unicode(u"День добрый")
    День добрый

    >>> n_cp1251 = make_encoding_normalizer(u"Привет!".encode('cp1251'))
    >>> print n_cp1251(u"День добрый".encode('cp1251'))
    День добрый
    >>> type(n_cp1251(u"День добрый".encode('cp1251')))
    <type 'unicode'>
    '''
    if isinstance(txt, unicode):
        return lambda text: text

    enc = chardet.detect(txt)['encoding']
    return lambda file_name: codecs.decode(file_name, enc)


for filename in sys.argv[1:]:
    archive = zipfile.ZipFile(filename, 'r')
    infolist = archive.infolist()

    probe_txt = "\n".join(i.filename for i in infolist)
    normalizer = make_encoding_normalizer(probe_txt)

    for i in infolist:
        print i.filename
        f = normalizer(i.filename)
        print f
        dirname = os.path.dirname(f)
        if dirname:
            assert os.path.abspath(dirname).startswith(os.path.abspath(".")), \
                "Security violation"
            if not os.path.exists(dirname):
                os.makedirs(dirname)
        if not f.endswith("/"):
            open(f, 'w').write(archive.read(i))
    archive.close()


if __name__ == '__main__' and len(sys.argv) == 1:
    # Hack for Python 2.x to support unicode source files as doctest sources.
    reload(sys)
    sys.setdefaultencoding("UTF-8")

    import doctest
    doctest.testmod()

    print "If there are no messages above, the script passes all tests."

ขอบคุณสำหรับโปรแกรมของคุณ! น่าเศร้าโปรแกรมซิปไม่ได้ทำงานภายใต้หลาม 3 แต่การทำงานภายใต้งูหลาม 2
beroal

@beroal ฉันอัพเดตสคริปต์ ตอนนี้มันใช้เครื่องมือที่พัฒนาโดย Mozilla สำหรับ Firefox เพื่อตรวจจับการเข้ารหัสโดยอัตโนมัติ
dmitry_romanov

4

POSIX-1.2001 ระบุวิธีที่ TAR ใช้ UTF-8

ตั้งแต่ปี 2007 changelog เวอร์ชั่น 6.3.0 ใน PKZIP APPNOTE.TXT ( http://www.pkware.com/documents/casestudies/APPNOTE.TXT ) ระบุว่า ZIP ใช้ UTF-8 อย่างไร

มันเป็นเพียงเครื่องมือที่สนับสนุนมาตรฐานเหล่านี้อย่างถูกต้องซึ่งยังคงเป็นคำถามเปิด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.