BSachin
BSachin

Reputation: 21

f string vs .format in manipulating a file name

I am trying to manipulate a file name to remove an underscore.

final_name = '{0}{1}{2}_{4}_new'.format(*filename.split('_'))

The filename is something like - 2024_10_10_091530_xyz_new.txt and the code does work fine and I can get the desired result 20241010_xyz_new as the final name.

How would I be able to get the same result using an f-string?

Upvotes: 0

Views: 87

Answers (4)

ShadowRanger
ShadowRanger

Reputation: 155584

Generally you would two-line it, one to split and unpack, one to use the unpacked names:

# Limit to 5 splits to avoid oversplitting when suffix name has additional
# underscores you don't care about
year, month, day, _, name, _ = filename.split('_', 5)
final_name = f'{year}{month}{day}_{name}_new'

The first line just splits it up and unpacks the result (ignoring the two components of the original name we don't care about, but still listing them to ensure they actually exist as expected). Additional underscores are allowed to exist in the ignored suffix, we avoid them causing unpacking exceptions (and speed up the split very slightly by avoiding unnecessary work looking for them) by explicitly limiting to no more than five splits.

After that, we have access to all of them as named variables (making the intent of the code rather more obvious) and can use those names in the f-string placeholders.

Note that this code is rather brittle as written. We claim it's getting year, month, day, etc., but all we really checked is the underscore count is at least five (if it was less than five, the unpacking would die with an exception). This is a case where a more rigorous check via a regex might help you, e.g.:

import re  # At top of file

# Adjust regex to be more or less permissive as appropriate, e.g.
# allowing fewer digits in date components if that's expected,
# allowing wider ranger of characters in "name" component, etc.
if m := re.fullmatch(r'(\d{4})_(\d{2})_(\d{2})_\d{6}_([a-z]+)_new.txt', filename):
    year, month, day, name = m.groups()
    final_name = f'{year}{month}{day}_{name}_new'
else:
    # Not in expected form handle error here

Upvotes: 3

ekhumoro
ekhumoro

Reputation: 120738

Use the walrus:

>>> filename = '2024_10_10_091530_xyz_new.txt'
>>> f'{(a:=filename.split('_'))[0]}{a[1]}{a[2]}_{a[4]}_{a[5]}'
'20241010_xyz_new.txt'

Upvotes: 0

LMC
LMC

Reputation: 12822

Assuming the date part has constant width

fn = '2024_10_10_091530_xyz_new.txt'
print(f"{fn[0:4]}{fn[5:7]}{fn[8:10]}{fn[17:]}")

Result

20241010_xyz_new.txt

Also this convoluted one

print(f"{''.join([c for i,c in enumerate(fn[:]) if i not in [4,7,10] and (i < 11 or i > 16)])}")

Upvotes: 0

Vikas Sharma
Vikas Sharma

Reputation: 2149

Like this:

filename = "2024_10_10_091530_xyz_new.txt"
parts = filename.split('_')
final_name = f"{''.join(parts[:3])}_{parts[4]}_new"
print(final_name)  
# Output: 20241010_xyz_new

Upvotes: 0

Related Questions