5

I am trying to write a block of code which opens a new file every time a Python3 script is run.

I am constructing the filename using an incrementing number.

For example, the following are some examples of valid filenames which should be produced:

output_0.csv
output_1.csv
output_2.csv
output_3.csv

On the next run of the script, the next filename to be used should be output_4.csv.

In C/C++ I would do this in the following way:

  • Enter an infinite loop
  • Try to open the first filename, in "read" mode
  • If the file is open, increment the filename number and repeat
  • If the file is not open, break out of the loop and re-open the file in "write" mode

This doesn't seem to work in Python 3, as opening a non-existing file in read mode causes an exception to be raised.

One possible solution might be to move the open file code block inside a try-catch block. But this doesn't seem like a particularly elegant solution.

Here is what I tried so far in code

# open a file to store output data
filename_base = "output"
filename_ext = "csv"
filename_number = 0

while True:
    filename_full = f"{filename_base}_{filename_number}.{filename_ext}"
    with open(filename_full, "r") as f:
        if f.closed:
            print(f"Writing data to {filename_full}")
            break
        else:
            print(f"File {filename_full} exists")
            filename_number += 1

with open(filename_full, "w") as f:
    pass

As explained above this code crashes when trying to open a file which does not exist in "read" mode.

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 4
    The exception is there to inform you "If the file is not open". Catching the exception is how you check it. There is nothing inelegant about this. – MisterMiyagi Jul 09 '21 at 12:27
  • 1
    This article may help you, https://dev.to/zhiyueyi/how-to-open-a-file-in-python-like-a-pro-3oe0 – Akshay Saambram Jul 09 '21 at 12:28

5 Answers5

4

Using pathlib you can check with Path.is_file() which returns True when it encounters a file or a symbolic link to a file.

from pathlib import Path

filename_base = "output"
filename_ext = "csv"
filename_number = 0

filename_full = f"{filename_base}_{filename_number}.{filename_ext}"
p = Path(filename_full)

while p.is_file() or p.is_dir():
    filename_number += 1
    p = Path(f"{filename_base}_{filename_number}.{filename_ext}")

This loop should exit when the file isn’t there so you can open it for writing.

Alex
  • 6,610
  • 3
  • 20
  • 38
  • A nice solution. I see only one potential issue, which is where something, like a directory, exists, and is not a file. Is there a version of this which uses something in place of `is_file()` to detect the presence of any path, rather than just files. (Simlinks, dirs, etc) – FreelanceConsultant Jul 09 '21 at 12:52
  • `is_file()` will return True for symbolic links, I've updated the answer a little to account for directories – Alex Jul 09 '21 at 13:09
2

you can check if a file exists prior using

os.path.exists(filename)
Ramtin Nouri
  • 310
  • 1
  • 8
1

You could use the OS module to check if the file path is a file, and then open it:

import os

file_path = './file.csv'

if(os.path.isfile(file_path)):
    with open(file_path, "r") as f:
andrepz
  • 443
  • 6
  • 16
1

This should work:

filename_base = "output"
filename_ext = "csv"
filename_number = 0

while True:
    filename_full = f"{filename_base}_{filename_number}.{filename_ext}"
    try:
      with open(filename_full, "r") as f:
          print(f"File {filename_full} exists")
          filename_number += 1
    except FileNotFoundError:
      print("Creating new file")
      open(filename_full, 'w');
      break;
Marko Borković
  • 1,884
  • 1
  • 7
  • 22
1

You might os.path.exists to check if file already exists for example

import os
print(os.path.exists("output_0.csv"))

or harness fact that your names

output_0.csv
output_1.csv
output_2.csv
output_3.csv

are so regular, exploit glob.glob like so

import glob
existing = glob.glob("output_*.csv")
print(existing)  # list of existing files
Daweo
  • 31,313
  • 3
  • 12
  • 25