I tried to solve some basic CS50 python problems and got stuck with this one (I know it can be done just by using ifs). Basically you need to print the file extension, so i split the filename to get ["filename", "extension"], and to this point it does work ok.
However no matter what i input it always print "error". Here's the code:
filename = input("Enter filename: ")
extension = filename.split(".")
match extension:
case ["jpg"]:
print("this is a jpg file")
case ["gif"]:
print("this is a gif file")
case _:
print("error")
I also tried some examples from different sites and it always prints the last case. Python version is 3.10.6. Any ideas?
extension is a list of the form ["filename", "extension"], it will never match a single-element list.
You need to match only on the second element:
case [_, "jpg"]:
print("this is a jpg file")
case [_, "gif"]:
print("this is a gif file")
However, if the filename includes multiple . then this will break because extension will have an unknown number of elements. This can be resolved in several ways:
Using *_ instead of _
Using os.path.splitext(filename)[-1] to get the extension (with the .) in the safest manner:
import os
extension = os.path.splitext(filename)[-1]
match extension:
case ".jpg":
print("this is a jpg file")
case ".gif":
print("this is a gif file")
case _:
print("error")
When you split your string, it creates a list to contain all the fragments.
extension = filename.split(".")
>>> print(type(extension), extension)
<class 'list'> ['hello', 'jpg']
You should select the specific string you are interested in. In this case, it would be the last element in the list, presumably. To achieve this, you could use:
extension[-1]
>>> print(type(extension[-1]), extension[-1])
<class 'str'> jpg
Issue: You are using split() which returns a list (e.g. ['data', 'gif']). You are comparing this with a list that contains just the file extension (e.g. ['gif']). This will result in False.
Assumption: The filename is valid and has an extension e.g. image.gif.
Solution 1 - Using index: In this case, the last element in the list is the extension so you can use this for matching. An index of -1 will return the last element in the list.
filename = input('Enter filename: ')
extension = filename.split('.')
match extension[-1]:
case 'jpg':
print('this is a jpg file')
case 'gif':
print('this is a gif file')
case _:
print('error')
Solution 2 - Using list: Alternatively, if you wish to use the result of split() directly then you can do so as follows:
filename = input('Enter filename: ')
extension = filename.split(".")
match extension:
case [*_, 'jpg']:
print('this is a jpg file')
case [*_, 'gif']:
print('this is a gif file')
case _:
print('error')
Note, we use *_ as there could be file names with multiple . thus resulting in a list with more than two items.
Match on Pathlib.suffix :
from pathlib import Path
filename = input("Enter filename: ")
extension = Path(filename).suffix
match extension:
case ".jpg":
print("this is a jpg file")
case ".gif":
print("this is a gif file")
case _:
print("error")
Yet another option is to use os.path.splitext on the file name - although it includes the dot:
import os
path = input("Enter path: ")
match os.path.splitext()[-1]:
case ".jpg":
print("jpg")
case ".png":
print("png")
If you want to also match double extensions like in data.tar.gz, you need to use str.split combined with str.join - additionally, this code also checks for filenames with a single leading dot (so not having an extension):
path = input("Enter path: ")
split = path.split(".")
if len(split) == 1 or os.path.split(path)[-1].startswith("."):
ext = ""
elif len(split) == 2:
ext = split[1]
else:
ext = split[-2] + "." + split[-1]
match ext:
case ".jpg":
print("jpg")
case ".png":
print("png")
Related
I have large jpeg files dataset.file name are like this 965_0000000005_0000000001_20211105153826.jpg
965_0000000005_0000000002_20211105153826.jpg
.
.
966_0000000005_0000000001_20211105153832.jpg
.
.
967_.........
.
.
so on...
I want to make the python script to copy the file by giving multiple inputs the file names according 965, 966, 988, ., .,.. and then it will copy to new folder.
I am trying to copy multiple image files from one folder to another using the following code
import os
import shutil
import datetime
def copyImage (num):
data = os.walk(os.path.normcase('./'))
flag = False
for root, dirs, files in data:
for file in files:
if (file.split('_')[0] == num and file.endswith('.jpg')):
flag = True
filename = os.path.join(root, file)
try:
shutil.copyfile(filename, str(datetime.date.today()) + slash + file)
except:
pass
if flag == True: print('OK==>No %s has been copied.' %num)
elif flag == False: print('NG==>No %s does not exist.' %num)
return
if __name__ == '__main__':
while True:
slash = os.path.normcase('/')
number = list(map(int, input("Enter multiple values: ").split()))
# print(number)
annotationNum=[]
for num in number:
# print(num)
num=str(num)
num=eval(num)
annotationNum.append(num)
# print(annotationNum)
if annotationNum =='q':
break
try:
os.makedirs(str(datetime.date.today()))
except:
pass
for num in annotationNum:
copyImage (num)
```
output is
Enter multiple values: 965 966 968 4
NG==>No 965 does not exist.
NG==>No 966 does not exist.
NG==>No 968 does not exist.
NG==>No 4 does not exist.
Enter multiple values: q
but I want the o/p
Enter multiple values: 965 966 968 4
OK==>No 965 has been copied.
OK==>No 966 has been copied.
OK==>No 968 has been copied.
NG==>No 4 does not exist.
Enter multiple values: q
Try using this script... I made some inline comments explaining any changes that I made. I wasnt sure what the 'q' conditional statement was for so you might want to put that back in if it was important.
import os
import shutil
import datetime
def copyImage (numbers, target): # send in the full list of numbers to avoid
# multiple function calls
# Also send in the target directory to avoid
# having to reconstruct it over and over
unused = numbers[:]
data = os.walk(os.path.normcase('./'))
for root, _, files in data:
for filename in files:
# the next line splits the filename just as before but instead of
# comparing it to one number, it checks if it matches any of the
# numbers in the number list
part = filename.split('_')[0]
if part in numbers and filename.endswith('.jpg'):
if part in unused:
unused.remove(part)
fullpath = os.path.join(root, filename)
try:
# Use the target directory passed as a parameter to
# construct the copy location
shutil.copyfile(fullpath, os.path.join(target, filename))
# output messages when they happen to avoid dealing with
# flag variables
print(f"OK==>No {fullpath} has been copied.")
except:
print(f"NG==>No {part} failed to copy")
for number in unused:
print(f"NG==>No {number} does not exist")
if __name__ == '__main__':
target = str(datetime.date.today()) # construct the output target directory
os.makedirs(target) # create the target directiory
numbers = input("Enter multiple values: ").split() # get input values and
# split them into a
# list but leave them
# as strings since
numbers = [i.strip() for i in numbers if i]
copyImage(numbers, target) # call function with the string list and
# the target directory
Here is a very simple way to do that!
First, to quit the while loop with 'q' we have to read users' inputs as str.
Next, by importing glob you can insert the names of the whole files (.jpg) to a list
Then, inside copyImage function, you need to change this to the folder name that you want to read your images from THE_NAME_OF_THE_FOLDER_TO_COPY_FROM
Now, using the power of List Comprehension we can do it by one line of code by looping in images and only do the copy when we find our intended num
Let me know if you need more explanation?
import shutil
import glob
import datetime
import os
def copyImage(num):
images = glob.glob("THE_NAME_OF_THE_FOLDER_TO_COPY_FROM/*.jpg")
# print(images)
[shutil.copy(img, str(datetime.date.today())) for img in images if img.split('/')[1].split('_')[0] == str(num)]
if __name__ == '__main__':
while True:
numbers = list(map(str, input("Enter multiple values: ").split()))
# print(numbers)
if 'q' in numbers:
print("Exit")
break
nums = [eval(number) for number in numbers]
# print(num)
try:
os.makedirs(str(datetime.date.today()))
except:
pass
for n in nums:
copyImage(n)
I am attempting to take a file name such as 'OP 40 856101.txt' from a directory, remove the .txt, set each single word to a specific variable, then reorder the filename based on a required order such as '856101 OP 040'. Below is my code:
import os
dir = 'C:/Users/brian/Documents/Moeller'
orig = os.listdir(dir) #original names of the files in the folder
for orig_name in orig: #This loop splits each file name into a list of stings containing each word
f = os.path.splitext(orig_name)[0]
sep = f.split() #Separation is done by a space
for t in sep: #Loops across each list of strings into an if statement that saves each part to a specific variable
#print(t)
if t.isalpha() and len(t) == 3:
wc = t
elif len(t) > 3 and len(t) < 6:
wc = t
elif t == 'OP':
op = t
elif len(t) >= 4:
pnum = t
else:
opnum = t
if len(opnum) == 2:
opnum = '0' + opnum
new_nam = '%s %s %s %s' % (pnum,op,opnum, wc) #This is the variable that contain the text for the new name
print("The orig filename is %r, the new filename is %r" % (orig_name, new_nam))
os.rename(orig_name, new_nam)
However I am getting an error with my last for loop where I attempt to rename each file in the directory.
FileNotFoundError: [WinError 2] The system cannot find the file specified: '150 856101 OP CLEAN.txt' -> '856101 OP 150 CLEAN'
The code runs perfectly until the os.rename() command, if I print out the variable new_nam, it prints out the correct naming order for all of the files in the directory. Seems like it cannot find the original file though to replace the filename to the string in new_nam. I assume it is a directory issue, however I am newer to python and can't seem to figure where to edit my code. Any tips or advice would be greatly appreciated!
Try this (just changed the last line):
os.rename(os.path.join(dir,orig_name), os.path.join(dir,new_nam))
You need to tell Python the actual path of the file to rename - otherwise, it looks only in the directory containing this file.
Incidentally, it's better not to use dir as a variable name, because that's the name of a built-in.
I have some files (800+) in folder as shown below:
test_folder
1_one.txt
2_two.txt
3_three.txt
4_power.txt
5_edge.txt
6_mobile.txt
7_test.txt
8_power1.txt
9_like.txt
10_port.txt
11_fire.txt
12_water.txt
I want to rename all these files using python like this:
test_folder
001_one.txt
002_two.txt
003_three.txt
004_power.txt
005_edge.txt
006_mobile.txt
007_test.txt
008_power1.txt
009_like.txt
010_port.txt
011_fire.txt
012_water.txt
Can we do this with Python? Please guide on how to do this.
Use zfill to pad zeros
import os,glob
src_folder = r"/user/bin/"
for file_name in glob.glob(os.path.join(src_folder, "*.txt")):
lst = file_name.split('_')
if len(lst)>1:
try:
value=int(lst[0])
except ValueError:
continue
lst[0] = lst[0].zfill(3)
os.rename(file_name, '_'.join(lst))
Using zfill:
Split based on underscore _ and then use zfill to pad zero's
import os
os.chdir("test_folder")
for filename in os.listdir("."):
os.rename(filename, filename.split("_")[0].zfill(3) + filename[filename.index('_'):])
Converting to integer:
Only renames if prefix is a valid integer. Uses format(num, '03') to make sure the integer is padded with appropriate leading zero's. Renames files 1_file.txt, 12_water.txt but skips a_baa.txt etc.
import os
os.chdir("E:\pythontest")
for filename in os.listdir("."):
try:
num = int(filename.split("_")[0])
os.rename(filename, format(num, '03') + filename[filename.index('_'):])
except:
print 'Skipped ' + filename
EDIT: Both snippets ensure that if the filename contains multiple underscores then the later ones aren't snipped. So 1_file_new.txt gets renamed to 001_file_new.txt.
Examples:
# Before
'1_one.txt',
'12_twelve.txt',
'13_new_more_underscores.txt',
'a_baaa.txt',
'newfile.txt',
'onlycharacters.txt'
# After
'001_one.txt',
'012_twelve.txt',
'013_new_more_underscores.txt',
'a_baaa.txt',
'newfile.txt',
'onlycharacters.txt'
Here's a quick example to rename the files in the current directory:
import os
for f in os.listdir("."):
if os.path.isfile(f) and len(f.split("_")) > 1:
number, suffix = f.split("_")
new_name = "%03d_%s" % (int(number), suffix)
os.rename(f, new_name)
You can use glob.glob() to get a list of text files. Then use a regular expression to ensure that the file being renamed starts with digits and an underscore. Then split the file up and add leading zeros as follows:
import re
import glob
import os
src_folder = r"c:\source folder"
for filename in glob.glob(os.path.join(src_folder, "*.txt")):
path, filename = os.path.split(filename)
re_file = re.match("(\d+)(_.*)", filename)
if re_file:
prefix, base = re_file.groups()
new_filename = os.path.join(path, "{:03}{}".format(int(prefix), base))
os.rename(filename, new_filename)
The {:03} tells Python to zero pad your number to 3 digits. Python's Format Specification Mini-Language is very powerful.
Note os.path.join() is used to safely concatenate path components, so you don't have to worry about trailing separators.
import os.path
import re
def request ():
print ("What file should I write to?")
file = input ()
thing = os.path.exists (file)
if thing == True:
start = 0
elif re.match ("^.+.\txt$", file):
stuff = open (file, "w")
stuff.write ("Some text.")
stuff.close ()
start = 0
else:
start = 1
go = "yes"
list1 = (start, file, go)
return list1
start = 1
while start == 1:
list1 = request ()
(start, file, go) = list1
Whenever I enter Thing.txt as the text, the elif should catch that it's in the format given. However, start doesn't change to 0, and a file isn't created. Have I formatted the re.match incorrectly?
"^.+.\txt$" is an incorrect pattern for match .txt files you can use the following regex :
r'^\w+\.txt$'
As \w matches word character if you want that the file name only contain letters you could use [a-zA-Z] instead :
r'^[a-zA-Z]+\.txt$'
Note that you need to escape the . as is a special sign in regular expression .
re.match (r'^\w+\.txt$',file)
But as an alternative answer for match file names with special format you can use endswith() :
file.endswith('.txt')
Also instead of if thing == True you can just use if thing : that is more pythonic !
You should escape second dot and unescape the "t" character:
re.match ("^.+\.txt$", file)
Also note that you don't really need regex for this, you can simply use endswith or search for module that can give you files extensions:
import os
fileName, fileExtension = os.path.splitext('your_file.txt')
fileExtension is .txt, which is exactly what you're looking for.
i have below code in which filenames are FR1.1.csv, FR2.0.csv etc. I am using these names to print in header row but i want to modify these name to FR1.1 , Fr2.0 and so on. Hence i am using strip function to remove .csv. when i have tried it at command prompt its working fine. But when i have added it to main script its not giving output.
for fname in filenames:
print "fname : ", fname
fname.strip('.csv');
print "after strip fname: ", fname
headerline.append(fname+' Compile');
headerline.append(fname+' Run');
output i am getting
fname :FR1.1.csv
after strip fname: FR1.1.csv
required output-->
fname :FR1.1.csv
after strip fname: FR1.1
i guess some indentation problem is there in my code after for loop.
plesae tell me what is the correct way to achive this.
Strings are immutable, so string methods can't change the original string, they return a new one which you need to assign again:
fname = fname.strip('.csv') # no semicolons in Python!
But this call doesn't do what you probably expect it to. It will remove all the leading and trailing characters c, s, v and . from your string:
>>> "cross.csv".strip(".csv")
'ro'
So you probably want to do
import re
fname = re.sub(r"\.csv$", "", fname)
Strings are immutable. strip() returns a new string.
>>> "FR1.1.csv".strip('.csv')
'FR1.1'
>>> m = "FR1.1.csv".strip('.csv')
>>> print(m)
FR1.1
You need to do fname = fname.strip('.csv').
And get rid of the semicolons in the end!
P.S - Please see Jon Clement's comment and Tim Pietzcker's answer to know why this code should not be used.
You probably should use os.path for path manipulations:
import os
#...
for fname in filenames:
print "fname : ", fname
fname = os.path.splitext(fname)[0]
#...
The particular reason why your code fails is provided in other answers.
change
fname.strip('.csv')
with
fname = fname.strip('.csv')