Stack Exchange
Insensitive case reading file in QGIS action
I wrote the code below for a QGIS action to extract a shapefile from a zip file and load it in a group.
from PyQt4.QtCore import QFileInfo,QSettings
from qgis.core import QgsRasterLayer, QgsCoordinateReferenceSystem
import zipfile

s = QSettings()
oldValidation = s.value( "/Projections/defaultBehaviour" )
s.setValue( "/Projections/defaultBehaviour", "useProject" )
vl = QgsMapLayerRegistry.instance().mapLayersByName('catalogo_CTR')[0]
qgis.utils.iface.setActiveLayer(vl)
root = QgsProject.instance().layerTreeRoot()
group_name = "CTRN_10000"
group = root.findGroup(group_name)
if group == None:
    group = root.addGroup("CTRN_10000")
else:
    pass


ctr= '\\\\rvphnas02pw\\Prodotti_Cartografici\\Dati_Vettoriali\\ctrn\\c0101_CartografiaBase\\c0101074_CTRN5000\\2008\\DB_SHAPE\\ovest\\[% A_CODICE %]o_p.zip'

fh = open(ctr, 'rb')
z = zipfile.ZipFile(fh)
for name in z.namelist():
    outpath = "D:\\zip\\"
    z.extract("[% A_CODICE %]o_p/fabbric.dbf", outpath)
    z.extract("[% A_CODICE %]o_p/FABBRIC.shx", outpath)
    z.extract("[% A_CODICE %]o_p/FABBRIC.shp", outpath)
fh.close()  


fileName = "D:\\zip\\[% A_CODICE %]o_p\\FABBRIC.shp"
fileInfo = QFileInfo(fileName)
baseName = '[% A_CODICE %]'
rlayer = QgsVectorLayer(fileName, baseName) #usare QgsVectorLayer nel caso si volesse  caricare un vettore
crs = QgsCoordinateReferenceSystem()
crs.createFromSrid(3003) 
rlayer.setCrs(crs)
QgsMapLayerRegistry.instance().addMapLayer(rlayer, False)
group.insertChildNode(-1, QgsLayerTreeLayer(rlayer)) 

s.setValue( "/Projections/defaultBehaviour", oldValidation )

It works but there is a problem.
The file FABBRIC.shp , .shx, dbf are written sometimes in uppercase and other times in lowercase (for example: FABBRIC.shp or fabbric.shp or fabbric.Shp...).
So  I should change this part of code:
   z.extract("[% A_CODICE %]o_p/fabbric.dbf", outpath)
   z.extract("[% A_CODICE %]o_p/FABBRIC.shx", outpath)
   z.extract("[% A_CODICE %]o_p/FABBRIC.shp", outpath)

and here:
fileName = "D:\\zip\\[% A_CODICE %]o_p\\FABBRIC.shp"

with "insensitive case" solution.
Maybe is something easy but I stopped here without a solution.

Just convert the characters of the output to lowercase before writing. If it is already lowercase, it won't hurt.

Thanks. Maybe in output I can do this but in 'z.extract' I must write the correct name of shapefiles and I can't modify the zipfiles .

OK. z.extract will create a file in the current working directory. Following that, you will have to read the file, convert its contents to lowercase, and rewrite it. Since you need to do that more than once, create a function that does the processing and call it as often as needed.

Perfect @martineau. Any ideas for the first part of problem? I have thousand of zipfile that contain shapefile "fabbric" written in many different ways.

I'am working in pyqgis (pyhton 2.7), Zipfiles are max 5mb containing 20 differente shapefiles. The file FABBRIC.shp , .shx, dbf are written sometimes in uppercase and other times in lowercase (for example: FABBRIC.shp or fabbric.shp or fabbric.Shp...).

I think I was misunderstood the problem. It's not the contents of the members that in the wrong case, it's that the name of the member is the wrong case. Correct?

Yes, Unfortunately the database has this problem.

You can call z.namelist() that will return a list of the actual archive members' names. Seems like with that information you should be able to determine what name to use to extract the desired member(s) via z.extract().

It's right but I wish there was an automatic procedure that open a shapefile, contained in a zipfile, when I click the corresponding poligon.

There will be one after you write a function that does it. ;-) An alternative would be to use try/except and first try extracting an all uppercase member name, and if that fails, then again using an all lowercase version of it.

This could be a possibility. Now I try. in the meantime I have written the code to rename the file estracted : 'path="D:\zip\[% A_CODICE %]o_p\"
l = os.listdir(path)
for file in l:
print (file)
os.rename(path+file, path+file.lower())'

Maybe I could see in how many different ways  the file is written and use as many "if" and "elif"

Perhaps, but that's potentially going to require a lot of elifs to cover all the possible combinations. Are you using try/except? Are you always looking for names comprised of exactly the letters "fabbric" + ("dbf" or "shx" or "shp") or could  "fabbric" be something else?

Only "fabbric" + file extension

In that case, do a case-insensitive search though the values returned by z.namelist() and use those that match.

Definitely it's hard for me. For now  I think it's better to extract all file in zip archive and then to rename to lowercase them and to open the ones I need

Might want to hold on—I'm working on answer to do what I just suggested.

Here's something that will determine the proper shapefile member names to use given an archive's namelist() as input. You should call z.extract() for each of the names it returns.
def find_shapefile_names(shapefilenames, namelist):
    """ Return matching shapefile member names in the namelist ignoring letter case.
        The names in the shapefilenames list should be provided in all lowercase,
    """
    matches = []
    for name in namelist:
        lowercase_name = name.lower()
        if any(shapefilename in lowercase_name for shapefilename in shapefilenames):
            matches.append(name)
            if len(matches) == len(shapefilenames):
                return matches
    else:
        raise NameError('all shapefile names were not found in the namelist')

if __name__ == '__main__':
    from random import randint

    def random_namelist(prefix, shapefilenames):
        """ Generate a list of mixed-case shapefile names with a prefix. """
        chars = list(','.join(shapefilenames))
        for _ in range(len(chars) // 2):  # change case of half the letters
            i = randint(0, len(chars)-1)
            chars[i] = chars[i].upper()
        names = ''.join(chars).split(',')
        return [prefix+name for name in names]

    basename = '[% A_CODICE %]o_p/'
    shapefilenames = 'fabbric.dbf', 'fabbric.shx', 'fabbric.shp'

    for testnum in range(3):
        namelist = random_namelist(basename, shapefilenames)
        print('namelist test {}:'.format(testnum))
        for name in find_shapefile_names(shapefilenames, namelist):
            print('  ' + name)
        print('')


Thanks . I will try to understand It. You are a master.

You're welcome. Note I just made a minor modification. The only part you really need to understand is the find_shapefile_names() function—the rest is for testing.
