How to loop this Python program - python

How can I loop this code so that it asks the user to search for another file if the file has not been found?
import os, sys
from stat import *
from os.path import join
lookfor = input("\nPlease enter file name you want to search? \n")
def search(directory):
for files in os.listdir(directory):
fileItem = os.path.join(directory, files)
fileItemStatInfo = os.stat(fileItem)
if S_ISDIR(fileItemStatInfo.st_mode):
search(fileItem)
elif S_ISREG(fileItemStatInfo.st_mode):
print("Searching", fileItem)
if lookfor in files:
print("\nThe File Has Been Found: %s" % join(directory, lookfor))
break

First, you need to make search return success or failure:
def search(directory):
for files in os.listdir(directory):
fileItem = os.path.join(directory, files)
fileItemStatInfo = os.stat(fileItem)
if S_ISDIR(fileItemStatInfo.st_mode):
return search(fileItem)
elif S_ISREG(fileItemStatInfo.st_mode):
print("Searching", fileItem)
if lookfor in files:
print("\nThe File Has Been Found: %s" % join(directory, lookfor))
return True
(Note that if we fall off the end of the for loop, we'll fall off the end of the function, which means we return None. Therefore, the function an only return True, with is truthy, or None, which is not.)
Now, you just loop until it returns something true:
while True:
lookfor=input("\nPlease enter file name you want to search? \n")
if search(lookfor):
break
print('Could not find that file, sorry. Try again.')
All that said, I don't think search does what you want it to—and you can simplify it tremendously, too.
First, I'm not sure whether you're trying to use lookfor as a global variable, or as a closure within the search function… but either way, you probably shouldn't be doing that. Pass it as an argument.
Also, having a variable named files that holds each filename, instead of a collection of them, is very confusing.
If you want to walk a directory tree recursively, use os.walk instead of trying to implement it yourself.
And you should probably be printing out the found filename, not the looked-for fragment.
Putting that together:
def search(lookfor, directory):
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
if lookfor in filename:
print("\nThe File Has Been Found: %s" % join(dirpath, filename))
return True
Of course in most real-life code, you'll probably want to return the pathname, so the rest of your code can use it (e.g., to open the file), instead of just printing it out:
def search(lookfor, directory):
for dirpath, dirnames, filenames in os.walk(directory):
for filename in filenames:
if lookfor in filename:
return join(dirpath, filename)
while True:
lookfor=input("\nPlease enter file name you want to search? \n")
path = search(lookfor)
if path:
break
print('Could not find that file, sorry. Try again.')
with open(path) as f:
pass # now we can actually use the file we searched so hard for

Related

Function to display files isn't working - Python

For my class I have to write code that will receive a pathname and then display the file contents if its a file or display each file and its contents if a directory is the pathname.
I wrote the following code, which works fine when I test it with my own local directory. But when I run the checks on my class portal it returns an error "No such file or directory".
Can anyone help me see what's wrong with my code?
import os
def openFyle(filename):
openFile = open(filename)
return openFile.read()
def displayFiles(filename):
if os.path.isdir(filename):
os.chdir(filename)
print("Directory name:", os.getcwd())
dirLyst = os.listdir(filename)
for file in dirLyst:
if os.path.isfile(file):
return print("File name:", file, openFyle(file))
elif os.path.exists(file) and os.path.isdir(file):
os.chdir(file)
displayFiles(os.getcwd())
else:
print("File name:", filename, openFyle(filename))
def main():
#while True:
filename = input("Enter a pathname: ")
displayFiles(filename)
if __name__ == "__main__":
main()
Using the os module:
import os
def print_all_file_contents(directory):
for root, dirs, files in os.walk(directory):
for file in files:
print(open(os.path.join(root, file)).read())
if __name__ == "__main__":
print_all_file_contents(input("Enter a directory:"))
What I would guess is, one of the tests they're using on your function is to send an invalid path. In your function, you only cover two cases: either its a path or its a file and you print out the file name with its contents. What if you cant find that file? That else should be an elif that does the os.path.isfile, and then an else in case its an invalid path.
This should fix it.
def displayFiles(filename):
if os.path.isdir(filename):
os.chdir(filename)
print("Directory name:", os.getcwd())
dirLyst = os.listdir(filename)
for file in dirLyst:
if os.path.isfile(file):
return print("File name:", file, openFyle(file))
elif os.path.exists(file) and os.path.isdir(file):
os.chdir(file)
displayFiles(os.getcwd())
elif os.path.isfile(filename):
print("File name:", filename, openFyle(filename))
else:
print("Invalid input")
. I think the portal runs automated tests that pass it path names.
Sure, but that doesn't prevent it from giving you invalid input data, such as non-str or Path-like objects or non existing locations
Also, rather than using chdir, create the absolute path as you go using path.join
def displayFiles(filename):
if filename and os.path.exists(filename):
if os.path.isdir(filename):
print("Directory name:", filename)
for file in os.listdir(filename):
child = os.path.join(filename,file)
displayFiles(child)
else:
print("File name:", filename, openFyle(filename))
else:
print("Invalid input")
Almost any time you're working with file system paths in Python, I recommend pathlib, because it's awesome. It doesn't make a huge difference this this case, but getting used to using it will make things much easier when you need to, say, join multiple paths together.
from pathlib import Path
def display_file(path):
print(f'File: {path}\n')
print(path.read_text())
print('\n\n')
if __name__ == '__main__':
path = Path(input('Enter a pathname: '))
if path.is_dir():
for item in path.iterdir():
# This avoids attempting to print hidden files, which may exist
# depending on the OS / filesystem.
if item.is_file() and not item.name.startswith('.'):
display_file(item)
elif path.is_file():
display_file(path)
The code only tries to print contents if the path is in fact a file or a directory; it displays nothing if passed an invalid or nonexistant path.

How to access the last filename in a directory in python

I'm trying to loop through some files in a directory. If the filename has two specific strings together, then I'm supposed to open and read those files for information. However, if none of the files have those two strings, I want to print an error message only once.
for filename in os.listdir(directory):
if filename.find("<string1>") != -1 and filename.find("<string2>") != -1:
#open file
else:
#print error message
I know doing this will print as many error messages as there are files in the directory (i.e. if there's 15 files with no matches, I'll get 15 error messages). But what I want is to only print an error message once after there aren't any matches in any of the N files in directory. I figured I could do something like this:
for filename in os.listdir(directory):
if filename.find("<string1>") != -1 and filename.find("<string2>") != -1:
#open file
else:
if filename[-1]: #if filename is last in directory
#print error message
But I've discovered this doesn't work. How would I get an error message to print only after the last filename has been read and doesn't match?
A simple solution would be to initialize some boolean flag before your for loop, e.g. found = false
If you find a file, set found = true. Then you can check the value of found after your for loop finishes and print the appropriate message based on its value.
Filter the list of files before the for-loop:
filenames = [fname for fname in os.listdir(directory)
if '<string1>' in fname and '<string2>' in fname]
if filenames:
for filename in filenames:
#open file
else:
#print error message
You can probably also use the glob module to get the filenames:
import glob
filenames = glob.glob(directory + '/*string1*string2*')
Another way is to use a variable to check if all the files have been processed. Checked and found it working in Python 2.7
import os
directory = "E:\\test\\"
files_count = len(os.listdir(directory))
files_processed = 0
for filename in os.listdir(directory):
if 'string1' in filename and 'string2' in filename:
#open file
print ("Opening file")
else:
files_processed = files_processed + 1
if (files_processed >= files_count):
print ("error message")
Not sure if this is extreme. But I'd make it a function and raise IOError.
Plus, i'd always use absolute path. Try the pathlib module too
import os
def get_files(directory):
for filename in os.listdir(directory):
if "string1" in filename and "string2" in filename:
yield filename
raise IOError("No such file")
for file in get_files('.'):
print(file)
# do stuff with file

using "With open" in python to write to another directory

I want to do the following:
1) Ask the user for input for a file path they wish a directory listing for.
2) Take this file path and enter the results, in a list, in a text file in the directory they input NOT the current directory.
I am very nearly there but the last step is that I can't seem to save the file to the directory the user has input only the current directory. I have set out the current code below (which works for the current directory). I have tried various variations to try and save it to the directory input by the user but to no avail - any help would be much appreciated.
CODE BELOW
import os
filenames = os.path.join(input('Please enter your file path: '))
with open ("files.txt", "w") as a:
for path, subdirs, files in os.walk(str(filenames)):
for filename in files:
f = os.path.join(path, filename)
a.write(str(f) + os.linesep)
I came across this link https://cmdlinetips.com/2012/09/three-ways-to-write-text-to-a-file-in-python/. I think your issue has something to do with you needing to provide the full path name and or the way you are using the close() method.
with open(out_filename, 'w') as out_file:
..
..
.. parsed_line
out_file.write(parsed_line)
You have to alter the with open ("files.txt", "w") as a: statement to not only include the filename, but also the path. This is where you should use os.path.join(). Id could be handy to first check the user input for existence with os.path.exists(filepath).
os.path.join(input(...)) does not really make sense for the input, since it returns a single str, so there are no separate things to be joined.
import os
filepath = input('Please enter your file path: ')
if os.path.exists(filepath):
with open (os.path.join(filepath, "files.txt"), "w") as a:
for path, subdirs, files in os.walk(filepath):
for filename in files:
f = os.path.join(path, filename)
a.write(f + os.linesep)
Notice that your file listing will always include a files.txt-entry, since the file is created before os.walk() gets the file list.
As ShadowRanger kindly points out, this LBYL (look before you leap) approach is unsafe, since the existence check could pass, although the file system is changed later while the process is running, leading to an exception.
The mentioned EAFP (it's easier to ask for forgiveness than permission) approach would use a try... except block to handle all errors.
This approach could look like this:
import os
filepath = input('Please enter your file path: ')
try:
with open (os.path.join(filepath, "files.txt"), "w") as a:
for path, subdirs, files in os.walk(filepath):
for filename in files:
f = os.path.join(path, filename)
a.write(f + os.linesep)
except:
print("Could not generate directory listing file.")
You should further refine it by catching specific exceptions. The more code is in the try block, the more errors unrelated to the directory reading and file writing are also caught and suppressed.
Move to the selected directory then do things.
Extra tip: In python 2 use raw_input to avoid special chars error like : or \ ( just use input in python 3 )
import os
filenames = raw_input('Please enter your file path: ')
if not os.path.exists(filenames):
print 'BAD PATH'
return
os.chdir(filenames)
with open ("files.txt", "w") as a:
for path, subdirs, files in os.walk('.'):
for filename in files:
f = os.path.join(path, filename)
a.write(str(f) + os.linesep)

Data Path for File Input Processing

I wanted to supply python with a windows 'data path' that could be used to set up input processing. I googled this with no luck, and now figure I am on my own.
There appears to be many ways of reading in a file with python, and after some frustration with "\" and "/" and windows path names I found a way to get my data path set up. It is not a general approach but should serve me well.
Related Questions: Is this code ugly? Is this a nonstandard method? Are there elegant features in 3.6 that should be used?
### Title: Process an input file using a 'data path' for a user on windows
import sys
import os
print("OK, starting program...")
file_processed = False
for path, dirs, files in os.walk("/Users/Mike"):
if file_processed: break
for file in files:
if file_processed: break
if file == 'seriousdata.txt':
my_path = os.path.abspath(path)
my_dsn = os.path.join(my_path, file)
print("Opening txt file " + my_dsn + " for input.")
with open(my_dsn) as da_lines:
textlines = (line.rstrip('\r\n') for line in da_lines)
for line in textlines:
print(line)
file_processed = True
if file_processed:
pass
else:
print("File not found")
print("OK, program execution has ended.")
sys.exit() # END SAMPLE CODE SNIPPET
From looking at your code, I'm assuming that you want to start at one directory, and move through each child directory, printing out the matching filename's contents if it is found.
If so, then this is way to do this with recursion:
import os
def recursive_list(path, filename):
files = os.listdir(path)
for name in files:
try:
p = os.path.join(path, name)
if os.path.isdir(p):
recursive_list(p, filename)
else:
if name == filename:
with open(p, "r") as f:
print(f.read())
except PermissionError:
pass
return
recursive_list("/home/jebby/Desktop","some_file.txt")
This will start out listing files in path. For every file that is found, if that file is a directory, then the function itself is called (Starting at the path to that folder). If filename matches the name of any file in the current directory, it will be printed (if the user has permissions for that file).
Otherwise, if you only want to read the filename from a known directory without walking down the directory tree:
import os
data_path = "/home/jebby/Desktop"
file_you_want = "filename.txt"
with open(os.path.join(data_path, file_you_want), "r") as f:
content = f.read()
print(content)
The main question would be : Do you know the location of the file?
Jebby has an answer to crawl through the directories.
Here is a solution without using "import os"
dir_fullpath = "c:/project_folder/data"
dir_path = "data"
filename = "file.txt"
try:
f = open(dir_path + "/" + filename, 'r')
# print("open " +dir_path + "\" + filename)
# data=[]
for line in f:
print (line.rstrip())
# data.append(line.rstrip())
f.close()
except IOError:
print("Fail to open" + dir_path + "\" + filename)

Traversing Directories

I am trying to write a python program that takes an input directory, and prints out all the .txt files that are in the directory. However, if there is another folder inside that one, it must do the same thing using recursion.
My problem is that is only does the .txt files and does not traverse further into the directory.
import os
path = input("What directory would you like to search?: ")
def traverse(path):
files = os.listdir(path)
for i in files:
if os.path.isdir(i) == True:
traverse(i)
elif i.endswith('.txt'):
print(i)
traverse(path)
What is the problem?
It looks like the reason your code is failing is because the if os.path.isdir(i) == True line always fails, regardless of whether or not the file is the directory. This is because the files variable stores relative paths rather than absolute paths, which causes the check to fail.
If you want to do it using the recursion method you gave, your code can be changed as follows:
import os
path = input("What directory would you like to search?: ")
def traverse(path):
files = os.listdir(path)
files = (os.path.join(os.path.abspath(path), file) for file in files)
for i in files:
if os.path.isdir(i) == True:
traverse(i)
elif i.endswith('.txt'):
print(i)
traverse(path)
Here is a better way to do it using fnmatch (adapted to suit the rest of your code from Use a Glob() to find files recursively in Python?). It will recursively search all files in the supplied directory, and match those that end with
import fnmatch
import os
path = input("What directory would you like to search?: ")
def traverse(path):
matches = []
for root, dirnames, filenames in os.walk(path):
for filename in fnmatch.filter(filenames, '*.txt'):
matches.append(os.path.join(root, filename))
print matches
traverse(path)
You are missing full path otherwise it's fine. see below
def traverse(path):
files = os.listdir(path)
for i in files:
if os.path.isdir(os.path.join(path,i)):
traverse(os.path.join(path,i))
elif i.endswith('.txt'):
print(os.path.join(path,i))

Categories