I have a large number of files that begin with numbers (e.g., 10admin_boundary_x) and would like to rename the files so that they do not begin with a digit (e.g., admin_boundary_x). I am working with shapefiles (.shp, .shx, .dbf, etc) and thought a python script could save me some time.

I haven't quite figured it out yet, but here is the code I've got so far:

import os

#read in file from the directory
for filename in os.listdir("."):
    
    f = open(filename, "w")
    
    i = True
	
    while i:
	#if filename starts with a digit, lose first index by renaming and 
        #try again
	while filename[0].isdigit():
	    
	    filename = filename[1:]
            
        os.rename(f, filename)
        
        i = False    
	        
print 'Any filename starting with a digit has been renamed.'

Thank you for your help!

Dani AI

Generated

Nice project. Two quick gotchas before you start: (1) with shapefiles you must rename the whole family together (.shp/.shx/.dbf/.prj and friends) or apps will stop seeing the dataset; and (2) check for name collisions first. was spot-on about building a plan before renaming, and / were right about not opening files and handling only files, not dirs.

Here is a small Python 3 script that renames shapefile components as a set, strips only leading digits, and does a dry run by default. It also aborts if two different inputs would become the same output (e.g., 01roads and 02roads).

from pathlib import Path

EXTS = {".shp",".shx",".dbf",".prj",".cpg",".sbn",".sbx"}
dry_run = True  # set to False after you review the plan

def strip_leading_digits(s):
    i = next((k for k,ch in enumerate(s) if not ch.isdigit()), len(s))
    return s[i:]

root = Path(".")
groups = {}
for p in root.iterdir():
    if p.is_file() and p.suffix.lower() in EXTS:
        groups.setdefault(p.stem, []).append(p)

plan = []
for old_stem, files in groups.items():
    new_stem = strip_leading_digits(old_stem)
    if new_stem and new_stem != old_stem:
        for f in files:
            plan.append((f, f.with_name(new_stem + f.suffix)))

# detect collisions and existing targets
targets = {}
for src, dst in plan:
    targets.setdefault(dst, []).append(src)
conflicts = {dst:srcs for dst,srcs in targets.items() if len(srcs) > 1 or dst.exists()}
if conflicts:
    for dst, srcs in conflicts.items():
        print("Collision:", dst.name, "<-", ", ".join(s.name for s in srcs))
    raise SystemExit("Aborted; resolve collisions and rerun.")

for src, dst in plan:
    print(f"{src.name} -> {dst.name}")
    if not dry_run:
        src.rename(dst)

Tips:

  • Run it in a backup copy first; then flip dry_run to False.
  • On Windows, collisions can be case-insensitive, so keep names unique ignoring case.
  • If a name is all digits, the script skips it; give those a manual stem.

Recommended Answers

All 6 Replies

Don't open the files for writing, it will erase the files content !!! In fact you don't need to open the files at all. I suggest that you first create a dictionary newname --> oldname, this allows to detect the potential name collisions before renaming anything. Here is a possible script

import os
import re
startdigits = re.compile(r"^\d+")

def create_map():
    result = dict()
    for filename in os.listdir("."):
        if not os.path.isfile(filename): # skip subdirectories
            continue
        newfilename = startdigits.sub("", filename)
        if newfilename in result or (newfilename != filename
                                    and os.path.isfile(newfilename)):
            raise Exception("Name collision detected for '%s'" % filename)
        if newfilename != filename:
            result[newfilename] = filename
            
if __name__ == "__main__":
    filemap = create_map()
    for new, old in filemap.items():
        os.rename(old, new)

Thanks for the help...but when I try running that code I get a SyntaxError: invalid syntax at line 11...

You don't save the original file name so there is no "copy from" name. Be sure to back up the directory before testing any rename code.

import os
 
#read in file from the directory
for filename in os.listdir("."):
 
#    f = open(filename, "w")
 
#    i = True
 
#    while i:  doesn't do anything
	#if filename starts with a digit, lose first index by renaming and 
        #try again
     new_filename = filename
     while new_filename[0].isdigit():
 
	    new_filename = new_filename[1:]
 
     if new_filename != filename:
         print "renaming %s to %s" % (filename, new_filename)
         os.rename(filename, new_filename)

 
print 'Any filename starting with a digit has been renamed.'

Thank you so much woooee! That helped immensely! :D

Thanks for the help...but when I try running that code I get a SyntaxError: invalid syntax at line 11...

Sorry, parenthise line 11 like this

if ((newfilename in result) or (newfilename != filename
                                    and os.path.isfile(newfilename))):
            raise Exception("Name collision detected for '%s'" % filename)

You don't save the original file name so there is no "copy from" name. Be sure to back up the directory before testing any rename code.

import os
 
#read in file from the directory
for filename in os.listdir("."):
 
#    f = open(filename, "w")
 
#    i = True
 
#    while i:  doesn't do anything
	#if filename starts with a digit, lose first index by renaming and 
        #try again
     new_filename = filename
     while new_filename[0].isdigit():
 
	    new_filename = new_filename[1:]
 
     if new_filename != filename:
         print "renaming %s to %s" % (filename, new_filename)
         os.rename(filename, new_filename)

 
print 'Any filename starting with a digit has been renamed.'

I would use built in lstrip():

import os
import string

for filename in os.listdir(os.curdir): 
    new_filename = filename.lstrip(string.digits)
    if new_filename != filename:
        if os.path.isdir(filename):
            print "Dir name not changed", filename, '->', new_filename
        elif os.path.isfile(new_filename):
            print "File name in use, not renamed", filename, '->', new_filename
        else:
            print "Renaming %s to %s" % (filename, new_filename)
            os.rename(filename, new_filename)
           
 
print 'Any filename starting with a digit has been renamed.'
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.