Unzipping shapefiles in Arc 9.3 - Python 2.5

1104
3
Jump to solution
04-20-2012 11:22 AM
markdenil
Occasional Contributor III
The method for unzipping files in python 2.5 (Arc9.3) is a bit round-about.
In 2.6 (Arc 10) one gets both an extract() and an extractall() method, either of which which work like a charm...

HOWEVER, in 2.5 one has to read each file in the zip into a disk file.
to wit:

z = r"C:\zipfile.zip" zf = zipfile.ZipFile(z, 'r') nameList = zf.namelist() for n in nameList:     print n     outFile = open("%s\\%s" % (r"C:", n), 'w')     outFile.write(zf.read(n))     outFile.close()


This extracts most of the shapefile, but the .shp is damaged. ArcCatalog gives it a question mark icon, and it won't open.
The .dbf seems fine (it can be read by itself), and the .prj and .xml are also readable, but the .shp is the wrong size.
It is, in fact, larger than it would be unzipping with WinZip, and larger than zf.printdir() reports inside the zip.
(about 8,200 bytes larger)

I have to support some legacy Arc 9.3 installations for a few more months, so it would be nice to know how I can pry a shapefile out of its zip without rim-wracking it.
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
Luke_Pinner
MVP Regular Contributor
The zipfile module is pure python. Get a copy of zipfile.py from python 2.6, rename it and distribute it with your own scripts. E.g. (assuming it is saved as zipfile26.py in the same directory as your code):
import zipfile26 z = r"C:\zipfile.zip" zf = zipfile26.ZipFile(z, 'r') zf.extractall(etc...)

You could also try subclassing ZipFile (2.5) and adding the extract, extractall and _extract_member methods copied from the 2.6 ZipFile class. Completely untested but something like the following *might* work...
import zipfile,os,shutil  class ZipFile26(zipfile.ZipFile):          def extract(self, member, path=None, pwd=None):         """Extract a member from the archive to the current working directory,            using its full name. Its file information is extracted as accurately            as possible. `member' may be a filename or a ZipInfo object. You can            specify a different directory using `path'.         """         if not isinstance(member, zipfile.ZipInfo):             member = self.getinfo(member)          if path is None:             path = os.getcwd()          return self._extract_member(member, path, pwd)      def extractall(self, path=None, members=None, pwd=None):         """Extract all members from the archive to the current working            directory. `path' specifies a different directory to extract to.            `members' is optional and must be a subset of the list returned            by namelist().         """         if members is None:             members = self.namelist()          for zipinfo in members:             self.extract(zipinfo, path, pwd)      def _extract_member(self, member, targetpath, pwd):         """Extract the ZipInfo object 'member' to a physical            file on the path targetpath.         """         # build the destination pathname, replacing         # forward slashes to platform specific separators.         # Strip trailing path separator, unless it represents the root.         if (targetpath[-1:] in (os.path.sep, os.path.altsep)             and len(os.path.splitdrive(targetpath)[1]) > 1):             targetpath = targetpath[:-1]          # don't include leading "/" from file name if present         if member.filename[0] == '/':             targetpath = os.path.join(targetpath, member.filename[1:])         else:             targetpath = os.path.join(targetpath, member.filename)          targetpath = os.path.normpath(targetpath)          # Create all upper directories if necessary.         upperdirs = os.path.dirname(targetpath)         if upperdirs and not os.path.exists(upperdirs):             os.makedirs(upperdirs)          if member.filename[-1] == '/':             if not os.path.isdir(targetpath):                 os.mkdir(targetpath)             return targetpath          source = self.open(member, pwd=pwd)         target = file(targetpath, "wb")         shutil.copyfileobj(source, target)         source.close()         target.close()          return targetpath

View solution in original post

0 Kudos
3 Replies
Luke_Pinner
MVP Regular Contributor
The zipfile module is pure python. Get a copy of zipfile.py from python 2.6, rename it and distribute it with your own scripts. E.g. (assuming it is saved as zipfile26.py in the same directory as your code):
import zipfile26 z = r"C:\zipfile.zip" zf = zipfile26.ZipFile(z, 'r') zf.extractall(etc...)

You could also try subclassing ZipFile (2.5) and adding the extract, extractall and _extract_member methods copied from the 2.6 ZipFile class. Completely untested but something like the following *might* work...
import zipfile,os,shutil  class ZipFile26(zipfile.ZipFile):          def extract(self, member, path=None, pwd=None):         """Extract a member from the archive to the current working directory,            using its full name. Its file information is extracted as accurately            as possible. `member' may be a filename or a ZipInfo object. You can            specify a different directory using `path'.         """         if not isinstance(member, zipfile.ZipInfo):             member = self.getinfo(member)          if path is None:             path = os.getcwd()          return self._extract_member(member, path, pwd)      def extractall(self, path=None, members=None, pwd=None):         """Extract all members from the archive to the current working            directory. `path' specifies a different directory to extract to.            `members' is optional and must be a subset of the list returned            by namelist().         """         if members is None:             members = self.namelist()          for zipinfo in members:             self.extract(zipinfo, path, pwd)      def _extract_member(self, member, targetpath, pwd):         """Extract the ZipInfo object 'member' to a physical            file on the path targetpath.         """         # build the destination pathname, replacing         # forward slashes to platform specific separators.         # Strip trailing path separator, unless it represents the root.         if (targetpath[-1:] in (os.path.sep, os.path.altsep)             and len(os.path.splitdrive(targetpath)[1]) > 1):             targetpath = targetpath[:-1]          # don't include leading "/" from file name if present         if member.filename[0] == '/':             targetpath = os.path.join(targetpath, member.filename[1:])         else:             targetpath = os.path.join(targetpath, member.filename)          targetpath = os.path.normpath(targetpath)          # Create all upper directories if necessary.         upperdirs = os.path.dirname(targetpath)         if upperdirs and not os.path.exists(upperdirs):             os.makedirs(upperdirs)          if member.filename[-1] == '/':             if not os.path.isdir(targetpath):                 os.mkdir(targetpath)             return targetpath          source = self.open(member, pwd=pwd)         target = file(targetpath, "wb")         shutil.copyfileobj(source, target)         source.close()         target.close()          return targetpath
0 Kudos
markdenil
Occasional Contributor III
a = [67,111,108]
print "%s%s%s" % (chr(a[0]), (chr(a[1])*2), chr(a[2]))
0 Kudos
Luke_Pinner
MVP Regular Contributor
Haha, thanks.
0 Kudos