'Generate 8.3 filenames with standard library

I would like to generate an 8.3 filename (as used on DOS/FAT) without the modules win32api or ctypes (neither works with my configuration).

Currently, the code is this:

def short_names(names):
    names2 = []
    for i in names:
        append_tilde = True
        
        b = set(".\"/\\[]:;=, ") # ."/\[]:;=,[space] (forbidden chars)
        old = i
        
        for char in b:
            i = i.replace(char, "")
        
        if i == old: append_tilde = False
        
        name_parts = i.split(sep=".")
        name = ''.join(name_parts[0:len(name_parts)-1])
        extension = name_parts[-1][0:3]
        
        if len(name) > 6:
            name = name[0:6]
            append_tilde = True
        
        if append_tilde:
            for j in range(1,10):
                if name.upper()+"~"+str(j) not in names2:
                    names2.append(name.upper() + "~" + str(j))
                    break
                    
        
    return names2

But it returns the "~1" part only, not the 6-character part plus "~1".

For the example input:

["Program Files", "ProgramData", "Programme", "Documents and Settings", "Dokumente und Einstellungen"] it returns ['~1', '~2', '~3']

Intended return value:

["PROGRA~1", "PROGRA~2", "PROGRA~3", "DOCUME~1", "DOKUME~1"]

Python version: Python 3.10.1 (v3.10.1:2cd268a3a9, Dec 6 2021, 14:28:59) [Clang 13.0.0 (clang-1300.0.29.3)] on darwin



Solution 1:[1]

The problem is in the way you try to split a filename into a base part and an extension.

If you call split('.') on a string that doesn't have a . in it, you get back a list with a single element - your original string. This means that name_parts[0:len(name_parts)-1] is the same as name_parts[0:0] which is an empty list. You're setting name to an empty string, while extension is set to the first 3 characters of the entire file name.

You need to detect the case where there was no . in the filename and treat it differently.

    name_parts = i.split(sep=".")
    if len(name_parts) <= 1:
        name = i
        extension = ''
    else:
        name = ''.join(name_parts[0:len(name_parts)-1])
        extension = name_parts[-1][0:3]

P.S. Python has some facilities to make this easier. Check out os.path or pathlib.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mark Ransom