276

I need to pass a file path name to a module. How do I build the file path from a directory name, base filename, and a file format string?

The directory may or may not exist at the time of call.

For example:

dir_name='/home/me/dev/my_reports'
base_filename='daily_report'
format = 'pdf'

I need to create a string '/home/me/dev/my_reports/daily_report.pdf'

Concatenating the pieces manually doesn't seem to be a good way. I tried os.path.join:

join(dir_name,base_filename,format)

but it gives

/home/me/dev/my_reports/daily_report/pdf
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Damon Julian
  • 3,829
  • 4
  • 29
  • 34

6 Answers6

426

This works fine:

os.path.join(dir_name, base_filename + '.' + filename_suffix)

Keep in mind that os.path.join() exists only because different operating systems use different path separator characters. It smooths over that difference so cross-platform code doesn't have to be cluttered with special cases for each OS. There is no need to do this for file name "extensions" (see footnote) because they are always preceded by a dot character, on every OS that implements them.

If using a function anyway makes you feel better (and you like needlessly complicating your code), you can do this:

os.path.join(dir_name, '.'.join((base_filename, filename_suffix)))

If you prefer to keep your code clean, simply include the dot in the suffix:

suffix = '.pdf'
os.path.join(dir_name, base_filename + suffix)

That approach also happens to be compatible with the suffix conventions in pathlib, which was introduced in python 3.4 a few years after this question was asked. New code that doesn't require backward compatibility can do this:

suffix = '.pdf'
pathlib.PurePath(dir_name, base_filename + suffix)

You might be tempted to use the shorter Path() instead of PurePath() if you're only handling paths for the local OS. I would question that choice, given the cross-platform issues behind the original question.

Warning: Do not use pathlib's with_suffix() for this purpose. That method will corrupt base_filename if it ever contains a dot.


Footnote: Outside of Microsoft operating systems, there is no such thing as a file name "extension". Its presence on Windows comes from MS-DOS and FAT, which borrowed it from CP/M, which has been dead for decades. That dot-plus-three-letters that many of us are accustomed to seeing is just part of the file name on every other modern OS, where it has no built-in meaning.

ʇsәɹoɈ
  • 22,757
  • 7
  • 55
  • 61
  • 9
    You mentioned that the OS separator may not be `.`. For this one can use `os.extsep`. – sjbx Mar 18 '13 at 14:25
  • 3
    I mentioned no such thing. – ʇsәɹoɈ Jun 11 '13 at 21:35
  • 8
    You went to some lengths to explain that 'File name "extensions" only have significant meaning on one major operating system (they're simply part of the file name on non-Windows systems), and their separator character is always a dot'. The OP also expressed they saw /pdf at the end. So you could have done `os.path.join(dir_name, base_filename, os.extsep, extension)`. Your answer is perfectly correct. – sjbx Jun 12 '13 at 14:00
  • 3
    Yeah, you're right, it gives back just a string so os.path.join(dir_name, ''.join([base_filename, os.extsep, extension])) should do it. Again, it doesn't undermine the correctness of your answer. – sjbx Jun 14 '13 at 08:09
  • os.path.join is the best solution on python – RFV Oct 13 '16 at 02:49
  • 3
    @sjbx you should put `+` between filename parts. `os.path.join()` adds OS-specific path separators(`/` for example) between the arguments (as @sәɹoɈ correctly have them in his/her answer. Thus the correct form of your code snippet is: `os.path.join(dir_name, base_filename + os.extsep + extension)` – Shayan Amani Jun 21 '19 at 14:54
  • Not to be too bitchy, but don't all these example need a variable to store a result in? As in: foo = os.path.join(dir_name, base_filename). That is not very clear with the posts. – Jiminion Apr 18 '23 at 20:27
70

In Python 3.4 and above, the pathlib standard library module can be used like so:

>>> from pathlib import Path
>>> dirname, filename, suffix = '/home/reports', 'daily', '.pdf'
>>> Path(dirname, filename).with_suffix(suffix)
PosixPath('/home/reports/daily.pdf')
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 3
    I find pathlib to be much more elegant than os.path.join, which seems pretty clunky by comparison. – pioniere May 06 '19 at 20:35
  • 1
    Doesn't work if you filename has a "." >>>filename2= 'daily.hourly' >>>Path(dirname, filename2).with_suffix(suffix) Output:WindowsPath('/home/reports/daily.pdf') – wontleave Jun 28 '19 at 06:27
  • 5
    @wontleave: If a filename already has a suffix, [`with_suffix()`](https://docs.python.org/3/library/pathlib.html#pathlib.PurePath.with_suffix) will substitute it instead of appending. You want something like `Path(dirname, filename2 + suffix)` – Eugene Yarmash Jun 28 '19 at 08:46
  • 1
    `pathlib` also offers a clever operator overload: `(Path(dirname) / filename).with_suffix(suffix)`. – Karl Knechtel Apr 04 '23 at 01:50
33
>>> import os
>>> os.path.join(dir_name, base_filename + "." + format)
'/home/me/dev/my_reports/daily_report.pdf'
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • thanks,but I was hoping there was a cleaner way of appending that extension..python even has a splitext function to cut off the extension..so there has to be something to do the reverse – Damon Julian Aug 20 '11 at 16:16
  • 2
    The splitext function retains the '.' at the front of the extension. This is likely the cleanest way to do it. If you want it to "look" cleaner in your code, I'd suggest using a function or a lambda function. – Vorticity Aug 20 '11 at 21:19
1

Why not just include the extension in the base filename?

dir_name='/home/me/dev/my_reports/'
base_filename='daily_report.pdf'
os.path.join(dir_name, base_filename)
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
-1
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.

BASE_DIR = Path(__file__).resolve().parent.parent
TEMPLATE_PATH = Path.joinpath(BASE_DIR,"templates")
print(TEMPLATE_PATH)
Ravi
  • 1,744
  • 2
  • 20
  • 37
-1
import os
def createfile(name, location, extension):
    print(name, extension, location)
    #starting creating a file with some dummy contents
    path = os.path.join(location, name + '.' + extension)
    f = open(path, "a")
    f.write("Your contents!! or whatever you want to put inside this file.")
    f.close()
    print("File creation is successful!!")

def readfile(name, location, extension):
    #open and read the file after the appending:
    path = os.path.join(location, name + '.' + extension)
    f = open(path, "r")
    print(f.read())

#pass the parameters here
createfile('test','./','txt')
readfile('test','./','txt')
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153