I am making code which generates a new text file with today's date each time it is run. For exemple today's file name would be 2020-10-05. I would like to increment it so that if the program is run one or more times the same day it becomes 2020-10-05_1, _2 etc..
I have this code that I found from another question and i've tried tinkering with it but I'm still stuck. The problem is here they convert the file name to an int 1,2,3 and this way it works but this isn't the result I want.
def incrementfile():
todayday = datetime.datetime.today().date()
output_folder = "//10.2.30.61/c$/Qlikview_Tropal/Raport/"
highest_num = 0
for f in os.listdir(output_folder):
if os.path.isfile(os.path.join(output_folder, f)):
file_name = os.path.splitext(f)[0]
try:
file_num = int(file_name)
if file_num > highest_num:
highest_num = file_num
except ValueError:
print("The file name %s is not an integer. Skipping" % file_name)
output_file = os.path.join(output_folder, str(highest_num + 1) + f"{todayday}" + ".txt")
return output_file
How can I modify this code so that the output I get in the end is something like 2020-10-05_0, _1, _2 etc.. ?
Thanks !
I strongly recommend you to use pathlib instead of os.path.join. This is more convenient.
def incrementfile():
td = datetime.datetime.today().date()
path = pathlib.Path("/tmp") #set your output folder isntead of /tmp
inc = len(list(path.glob(f"{td}*")))+1
outfile = path/f"{td}_{inc}.txt"
return outfile
Not a direct answer to your question, but instead of using _1, _2 etc, you could use a full timestamp with date and current time, which would avoid duplication, EG:
from datetime import datetime
t = str(datetime.now()).replace(":", "-").replace(" ", "_")
print(t)
Example output:
2020-10-05_13-06-53.825870
I think this will work-
import os
import datetime
#assuming files will be .txt format
def incrementfile():
output_folder = "//10.2.30.61/c$/Qlikview_Tropal/Raport/"
files=os.listdir(output_folder)
current_name=datetime.date.today().strftime('%Y-%m-%d_0')
current_num=1
def nameChecker(name,files):
return True if name +'.txt' in files else False
while namChecker(current_name,files):
current_name+='_'+str(current_num)
current_num+=1
return current_name+'.txt'
Related
I need to get date from file name in python code. I found many solutions, but from fixed name and date. But I dont know what the name of the file will be, date is changing. How to do that?
I have a code which is working for known file name (current date), file is called micro20230125.txt
import re
import os
from datetime import datetime
header = """#SANR0000013003;*;#CNR0010;*;#RINVAL-777.0;*;"""
current_timestamp = datetime.today().strftime('%Y%m%d')
input_file = "micro" + current_timestamp + ".txt"
output_file = os.path.splitext(input_file)[0] + ".zrxp"
with open(input_file, "r") as f:
first_line = f.readline().strip('\n')
text = re.search('(\d{6})', first_line).group(1)
text = header + "\n" + text + "\n"
with open(output_file, "w") as f:
f.write(text)
print(text)
`
but I dont need current date. I will get file with some random date, so how can I extract unknown date from file name? How to change this variable current_timestamp?
I tried to use regex but I messed something up
EDIT: DIFF CODE, SIMILAR PROBLEM:
I was dealing with this code and then realized: python doesnt know what those numbers in name represent, so why treat them like a date and complicate things? Those are just numbers. As a matter of fact, I need those numbers as long as full file name. So I came up with different code.
import re
import os
def get_numbers_from_filename(filename):
return re.search(r'\d+', filename).group(0) #returns only numbers
for filename in os.listdir("my path"):
print (get_numbers_from_filename(filename))
def get_numbers_from_filename(filename):
return re.search(r"(.)+", filename).group(0) #returns all name
for filename in os.listdir("my path"):
print(get_numbers_from_filename(filename))
file was: micro20230104.txt
and result is:
result
Now, I want to use that result, dont want to print it.
No matter how I get that returns me error.
import re
import os
def get_numbers_from_filename(filename):
return re.search(r"(.)+", filename).group(0)
for filename in os.listdir("my path"):
print(get_numbers_from_filename(filename))
m = get_numbers_from_filename(filename)
output_file = os.path.splitext(m)[0] + ".zrxp"
with open(m, "r") as f:
first_line = f.readline().strip('\n')
text = re.search('(\d{6})', first_line).group(1)
text = header + "\n" + text + "\n"
with open(output_file, "w") as f:
f.write(text)
print(text)
but it it says error
error:there is no such file
what to do? what am I doing wrong?
Well, in case all the files have the format 'micro[YearMonthDay].txt', you can try this solution:
import os
from datetime import datetime
header = """#SANR0000013003;*;#CNR0010;*;#RINVAL-777.0;*;"""
#Change the variable folder_path for your actual directory path.
folder_path = "\\path_files\\"
filenames = []
# Iterate directory
for path in os.listdir(folder_path):
# check if current path is a file
if os.path.isfile(os.path.join(folder_path, path)):
filenames.append(path)
dates = []
for filename in filenames:
# First solution:
filename = filename.replace('micro', '')
filename = filename.replace('.txt', '')
date = datetime.strptime(filename, "%Y%m%d")
# Second solution:
# date = datetime.strptime(filename, "micro%Y%m%d.txt")
dates.append(date)
for date in dates:
print(date.strftime("%Y/%m/%d"))
with open(f'.\\micro{date.strftime("%Y/%m/%d")}.txt', "r") as f:
first_line = f.readline().strip('\n')
text = re.search('(\d{6})', first_line).group(1)
text = header + "\n" + text + "\n"
with open(output_file, "w") as f:
f.write(text)
print(text)
Use the solution you prefer and comment the other one.
Testing:
Text files for test
Code
Result
I hope I could help! :D
Does Python have any built-in functionality to add a number to a filename if it already exists?
My idea is that it would work the way certain OS's work - if a file is output to a directory where a file of that name already exists, it would append a number or increment it.
I.e: if "file.pdf" exists it will create "file2.pdf", and next time "file3.pdf".
I ended up writing my own simple function for this. Primitive, but gets the job done:
def uniquify(path):
filename, extension = os.path.splitext(path)
counter = 1
while os.path.exists(path):
path = filename + " (" + str(counter) + ")" + extension
counter += 1
return path
In a way, Python has this functionality built into the tempfile module. Unfortunately, you have to tap into a private global variable, tempfile._name_sequence. This means that officially, tempfile makes no guarantee that in future versions _name_sequence even exists -- it is an implementation detail.
But if you are okay with using it anyway, this shows how you can create uniquely named files of the form file#.pdf in a specified directory such as /tmp:
import tempfile
import itertools as IT
import os
def uniquify(path, sep = ''):
def name_sequence():
count = IT.count()
yield ''
while True:
yield '{s}{n:d}'.format(s = sep, n = next(count))
orig = tempfile._name_sequence
with tempfile._once_lock:
tempfile._name_sequence = name_sequence()
path = os.path.normpath(path)
dirname, basename = os.path.split(path)
filename, ext = os.path.splitext(basename)
fd, filename = tempfile.mkstemp(dir = dirname, prefix = filename, suffix = ext)
tempfile._name_sequence = orig
return filename
print(uniquify('/tmp/file.pdf'))
I was trying to implement the same thing in my project but #unutbu's answer seemed too 'heavy' for my needs so I came up with following code finally:
import os
index = ''
while True:
try:
os.makedirs('../hi'+index)
break
except WindowsError:
if index:
index = '('+str(int(index[1:-1])+1)+')' # Append 1 to number in brackets
else:
index = '(1)'
pass # Go and try create file again
Just in case someone stumbled upon this and requires something simpler.
If all files being numbered isn't a problem, and you know beforehand the name of the file to be written, you could simply do:
import os
counter = 0
filename = "file{}.pdf"
while os.path.isfile(filename.format(counter)):
counter += 1
filename = filename.format(counter)
recently I encountered the same thing and here is my approach:
import os
file_name = "file_name.txt"
if os.path.isfile(file_name):
expand = 1
while True:
expand += 1
new_file_name = file_name.split(".txt")[0] + str(expand) + ".txt"
if os.path.isfile(new_file_name):
continue
else:
file_name = new_file_name
break
Let's say you already have those files:
This function generates the next available non-already-existing filename, by adding a _1, _2, _3, ... suffix before the extension if necessary:
import os
def nextnonexistent(f):
fnew = f
root, ext = os.path.splitext(f)
i = 0
while os.path.exists(fnew):
i += 1
fnew = '%s_%i%s' % (root, i, ext)
return fnew
print(nextnonexistent('foo.txt')) # foo_3.txt
print(nextnonexistent('bar.txt')) # bar_1.txt
print(nextnonexistent('baz.txt')) # baz.txt
Since the tempfile hack A) is a hack and B) still requires a decent amount of code anyway, I went with a manual implementation. You basically need:
A way to Safely create a file if and only if it does not exist (this is what the tempfile hack affords us).
A generator for filenames.
A wrapping function to hide the mess.
I defined a safe_open that can be used just like open:
def iter_incrementing_file_names(path):
"""
Iterate incrementing file names. Start with path and add " (n)" before the
extension, where n starts at 1 and increases.
:param path: Some path
:return: An iterator.
"""
yield path
prefix, ext = os.path.splitext(path)
for i in itertools.count(start=1, step=1):
yield prefix + ' ({0})'.format(i) + ext
def safe_open(path, mode):
"""
Open path, but if it already exists, add " (n)" before the extension,
where n is the first number found such that the file does not already
exist.
Returns an open file handle. Make sure to close!
:param path: Some file name.
:return: Open file handle... be sure to close!
"""
flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
if 'b' in mode and platform.system() == 'Windows':
flags |= os.O_BINARY
for filename in iter_incrementing_file_names(path):
try:
file_handle = os.open(filename, flags)
except OSError as e:
if e.errno == errno.EEXIST:
pass
else:
raise
else:
return os.fdopen(file_handle, mode)
# Example
with safe_open("some_file.txt", "w") as fh:
print("Hello", file=fh)
I haven't tested this yet but it should work, iterating over possible filenames until the file in question does not exist at which point it breaks.
def increment_filename(fn):
fn, extension = os.path.splitext(path)
n = 1
yield fn + extension
for n in itertools.count(start=1, step=1)
yield '%s%d.%s' % (fn, n, extension)
for filename in increment_filename(original_filename):
if not os.isfile(filename):
break
This works for me.
The initial file name is 0.yml, if it exists, it will add one until meet the requirement
import os
import itertools
def increment_filename(file_name):
fid, extension = os.path.splitext(file_name)
yield fid + extension
for n in itertools.count(start=1, step=1):
new_id = int(fid) + n
yield "%s%s" % (new_id, extension)
def get_file_path():
target_file_path = None
for file_name in increment_filename("0.yml"):
file_path = os.path.join('/tmp', file_name)
if not os.path.isfile(file_path):
target_file_path = file_path
break
return target_file_path
import os
class Renamer():
def __init__(self, name):
self.extension = name.split('.')[-1]
self.name = name[:-len(self.extension)-1]
self.filename = self.name
def rename(self):
i = 1
if os.path.exists(self.filename+'.'+self.extension):
while os.path.exists(self.filename+'.'+self.extension):
self.filename = '{} ({})'.format(self.name,i)
i += 1
return self.filename+'.'+self.extension
I found that the os.path.exists() conditional function did what I needed. I'm using a dictionary-to-csv saving as an example, but the same logic could work for any file type:
import os
def smart_save(filename, dict):
od = filename + '_' # added underscore before number for clarity
for i in np.arange(0,500,1): # I set an arbitrary upper limit of 500
d = od + str(i)
if os.path.exists(d + '.csv'):
pass
else:
with open(d + '.csv', 'w') as f: #or any saving operation you need
for key in dict.keys():
f.write("%s,%s\n"%(key, dictionary[key]))
break
Note: this appends a number (starting at 0) to the file name by default, but it's easy to shift that around.
This function validates if the file name exists using regex expresion and recursion
def validate_outfile_name(input_path):
filename, extension = os.path.splitext(input_path)
if os.path.exists(input_path):
output_path = ""
pattern = '\([0-9]\)'
match = re.search(pattern, filename)
if match:
version = filename[match.start() + 1]
try: new_version = int(version) + 1
except: new_version = 1
output_path = f"{filename[:match.start()]}({new_version}){extension}"
output_path = validate_outfile_name(output_path)
else:
version = 1
output_path = f"{filename}({version}){extension}"
return output_path
else:
return input_path
I've implemented a similar solution with pathlib:
Create file-names that match the pattern path/<file-name>-\d\d.ext. Perhaps this solution can help...
import pathlib
from toolz import itertoolz as itz
def file_exists_add_number(path_file_name, digits=2):
pfn = pathlib.Path(path_file_name)
parent = pfn.parent # parent-dir of file
stem = pfn.stem # file-name w/o extension
suffix = pfn.suffix # NOTE: extension starts with '.' (dot)!
try:
# search for files ending with '-\d\d.ext'
last_file = itz.last(parent.glob(f"{stem}-{digits * '?'}{suffix}"))
except:
curr_no = 1
else:
curr_no = int(last_file.stem[-digits:]) + 1
# int to string and add leading zeros
curr_no = str(last_no).zfill(digits)
path_file_name = parent / f"{stem}-{curr_no}{suffix}"
return str(path_file_name)
Pls note: That solution starts at 01 and will only find file-pattern containing -\d\d!
def create_file():
counter = 0
filename = "file"
while os.path.isfile(f"dir/{filename}{counter}.txt"):
counter += 1
print(f"{filename}{counter}.txt")
A little bit later but there is still something like this should work properly, mb it will be useful for someone.
You can use built-in iterator to do this ( image downloader as example for you ):
def image_downloader():
image_url = 'some_image_url'
for count in range(10):
image_data = requests.get(image_url).content
with open(f'image_{count}.jpg', 'wb') as handler:
handler.write(image_data)
Files will increment properly. Result is:
image.jpg
image_0.jpg
image_1.jpg
image_2.jpg
image_3.jpg
image_4.jpg
image_5.jpg
image_6.jpg
image_7.jpg
image_8.jpg
image_9.jpg
Easy way for create new file if this name in your folder
if 'sample.xlsx' in os.listdir('testdir/'):
i = 2
while os.path.exists(f'testdir/sample ({i}).xlsx'):
i += 1
wb.save(filename=f"testdir/sample ({i}).xlsx")
else:
wb.save(filename=f"testdir/sample.xlsx")
Is there a specific argument in open() built-in function so that if the filename already exists, it creates a file by adding a number to its name ??
such that if "file.txt" exists, it automatically creates "file-01.txt"
Or any other solution.!
No, I don't think there's something like this but you can do it yourself using os.path.isfile :
import os
filename = "yourFileName.txt"
if os.path.isfile(filename): #check if filename exists in the directory
filename = filename.split(".")[:-1] + "-01" + filename.split(".")[-1]
with open(filename, "w+") as f:
f.write(yourString)
I have found a solution, Thanks!!
b = True
c = 1
while b:
f_name = 'Task-{:02.0f}.txt'.format(c)
try:
f = open(f_name,'x')
b = False
except FileExistsError:
c += 1
f.close()
Something like this?
import os
if os.path.exists(filename):
fileparts = filename.split('.')
filename = fileparts[0] + '01.'
for a in fileparts[1:]:
filename += a
See what you think of this. ...this is what I use to do what you're looking for. It's the smallest way I found to solve the problem before, and is easy to wrap into a function:
import os
name = 'blah.txt'
uniq_name = name
while os.path.isfile(uniq_name):
# if increment variable 'delta' isn't defined, make it 1. Otherwise increment
delta = delta+1 if 'delta' in vars() else 1
uniq_name = f'{os.path.splitext(name)[0]}-{delta}{os.path.splitext(name)[1]}'
# this you don't need - it's just equivalent to a 'touch' command to show
# the output
open(uniq_name, 'a').close()
I'm trying to extract files from a zip file using Python 2.7.1 (on Windows, fyi) and each of my attempts shows extracted files with Modified Date = time of extraction (which is incorrect).
import os,zipfile
outDirectory = 'C:\\_TEMP\\'
inFile = 'test.zip'
fh = open(os.path.join(outDirectory,inFile),'rb')
z = zipfile.ZipFile(fh)
for name in z.namelist():
z.extract(name,outDirectory)
fh.close()
I also tried using the .extractall method, with the same results.
import os,zipfile
outDirectory = 'C:\\_TEMP\\'
inFile = 'test.zip'
zFile = zipfile.ZipFile(os.path.join(outDirectory,inFile))
zFile.extractall(outDirectory)
Can anyone tell me what I'm doing wrong?
I'd like to think this is possible without having to post-correct the modified time per How do I change the file creation date of a Windows file?.
Well, it does take a little post-processing, but it's not that bad:
import os
import zipfile
import time
outDirectory = 'C:\\TEMP\\'
inFile = 'test.zip'
fh = open(os.path.join(outDirectory,inFile),'rb')
z = zipfile.ZipFile(fh)
for f in z.infolist():
name, date_time = f.filename, f.date_time
name = os.path.join(outDirectory, name)
with open(name, 'wb') as outFile:
outFile.write(z.open(f).read())
date_time = time.mktime(date_time + (0, 0, -1))
os.utime(name, (date_time, date_time))
Okay, maybe it is that bad.
Based on Jia103's answer, I have developed a function (using Python 2.7.14) which preserves directory and file dates AFTER everything has been extracted. This isolates any ugliness in the function, and you can also use zipfile.Zipfile.extractAll() or whatever zip extract method you want:
import time
import zipfile
import os
# Restores the timestamps of zipfile contents.
def RestoreTimestampsOfZipContents(zipname, extract_dir):
for f in zipfile.ZipFile(zipname, 'r').infolist():
# path to this extracted f-item
fullpath = os.path.join(extract_dir, f.filename)
# still need to adjust the dt o/w item will have the current dt
date_time = time.mktime(f.date_time + (0, 0, -1))
# update dt
os.utime(fullpath, (date_time, date_time))
To preserve dates, just call this function after your extract is done.
Here's an example, from a script I wrote to zip/unzip game save directories:
z = zipfile.ZipFile(zipname, 'r')
print 'I have opened zipfile %s, ready to extract into %s' \
% (zipname, gamedir)
try: os.makedirs(gamedir)
except: pass # Most of the time dir already exists
z.extractall(gamedir)
RestoreTimestampsOfZipContents(zipname, gamedir) #<-- USED
print '%s zip extract done' % GameName[game]
Thanks everyone for your previous answers!
Based on Ethan Fuman's answer, I have developed this version (using Python 2.6.6) which is a little more consise:
zf = ZipFile('archive.zip', 'r')
for zi in zf.infolist():
zf.extract(zi)
date_time = time.mktime(zi.date_time + (0, 0, -1))
os.utime(zi.filename, (date_time, date_time))
zf.close()
This extracts to the current working directory and uses the ZipFile.extract() method to write the data instead of creating the file itself.
Based on Ber's answer, I have developed this version (using Python 2.7.11), which also accounts for directory mod dates.
from os import path, utime
from sys import exit
from time import mktime
from zipfile import ZipFile
def unzip(zipfile, outDirectory):
dirs = {}
with ZipFile(zipfile, 'r') as z:
for f in z.infolist():
name, date_time = f.filename, f.date_time
name = path.join(outDirectory, name)
z.extract(f, outDirectory)
# still need to adjust the dt o/w item will have the current dt
date_time = mktime(f.date_time + (0, 0, -1))
if (path.isdir(name)):
# changes to dir dt will have no effect right now since files are
# being created inside of it; hold the dt and apply it later
dirs[name] = date_time
else:
utime(name, (date_time, date_time))
# done creating files, now update dir dt
for name in dirs:
date_time = dirs[name]
utime(name, (date_time, date_time))
if __name__ == "__main__":
unzip('archive.zip', 'out')
exit(0)
Since directories are being modified as the extracted files are being created inside them, there appears to be no point in setting their dates with os.utime until after the extraction has completed, so this version caches the directory names and their timestamps till the very end.
I have spent the day trying to figure out how to export out a MS Excel File as a PDF. I am in desperate need of someone smarter than I:
Here is what I have so far and the error I get:
import os
import win32com.client
import win32com.client.dynamic
import datetime
import time
#
#Path to Read from where you want all the files read from
InputWkbkPath = "O:/MIS/Reporting/Field Reports/2014_Template_Files/w_code/"
obj = win32com.client.Dispatch("Outlook.Application")
xlApp = win32com.client.DispatchEx('Excel.Application')
OutputWkbkPath ='O:/MIS/Reporting/Field Reports/2015_Template_Files/Directors /Templates/20150123_Archive/'
for subdir, dirs, files in os.walk(InputWkbkPath):
for file in files:
#print os.path.join(subdir, file)
ip= os.path.join(subdir, file)
xlwb= xlApp.Workbooks.Open(ip)
#print xlwb
currentyear = datetime.date.today().strftime("%Y")
currentmonth = datetime.date.today().strftime("%B")
currentday = datetime.date.today().strftime("%d")
currentdate = currentmonth+"-"+currentday+"-"+currentyear
participant = xlwb.Worksheets(1).Range("C4").Value
title = xlwb.Worksheets(1).Range("C5").Value
StaffCode = xlwb.Worksheets(1).Range("C6").Value
OfficeName = xlwb.Worksheets(1).Range("C7").Value
LOCode = xlwb.Worksheets(1).Range("C8").Value
Region = xlwb.Worksheets(1).Range("C9").Value
ESN = str(xlwb.Worksheets(1).Range("C10").Value)
ParticipantEmail= xlwb.Worksheets(1).Range("C11").Value
MDEmail= xlwb.Worksheets(1).Range("C12").Value
RVPEmail = xlwb.Worksheets(1).Range("C13").Value
if title == "Director" or title == "DIRECTOR":
FileName = LOCode+"_"+participant+"_"+ESN+"_Comp_Model_"+currentdate+".xlsx"
#print FileName
else:
FileName = Region+"_"+LOCode+"_"+participant+"_"+ESN+"_Comp_Model_"+currentdate+".pdf"
OutputFile=OutputWkbkPath+FileName
xlwb.Worksheets(1).Activate
#print OutputFile
ws=xlwb.Worksheets(1)
ws.Visible = 1
xlwb.ExportAsFixedFormat(Type="xlTypePDF",OutputFile)
xlwb.Close(True)
I get the following error:
C:\Python27\python.exe C:/Users/username/PycharmProjects/File_Names/Loop_Throug_Open.py
File "C:/Users/username/PycharmProjects/File_Names/Loop_Throug_Open.py", line 55
xlwb.ExportAsFixedFormat(Type="xlTypePDF",OutputFile)
SyntaxError: non-keyword arg after keyword arg
Process finished with exit code 1
Please help. I can not find any on it in the groups.
Thank you ahead of time.
Robert
The problem was in the ExportAsFixedFormat method:
I changed to the following:
xlwb.ExportAsFixedFormat(0, OutputFile)
I also had to put the path with double regular slashes. So the outputWkbkPath looks like the following:
OutputWkbkPath ='O:\\MIS/Reporting\\Field Bonus Plan Reports\\2015__Files\\ DirectorsTemplates\\20150123_Archive\\'
I hope this helps someone else. The following post actually got me there:
.xlsx and xls(Latest Versions) to pdf using python
It is not the same package/module but that part works.