2
\$\begingroup\$

I am trying to write a code that should look on the laptop for a file, open it, if found, and read the context of that file (it should be .txt or .doc). So far, I wrote this code which is working:

def search(drive, target):
    file_path = ''
    if drive == "D":
        for root, dirs, files in os.walk(r"D:\\"):
            for name in files:
                if name == target:
                    file_path = os.path.abspath(os.path.join(root, name))
    elif drive == "C":
        for root, dirs, files in os.walk(r"C:\\"):
            for name in files:
                if name == target:
                    file_path = os.path.abspath(os.path.join(root, name))
    return file_path



def path():
    file_name = input("Enter the name of the desired file: ")
    path = os.path.abspath(file_name)
    file_path = search(path[0], file_name)
    return file_path

The problem is that this piece of code is very slow, as it has to look on each directory on the drive (the only trick I used to save some time was to take the abspath and to look on what drive it is saved, thus it doesn't look on each existing drive).

I am thinking of using multiprocessing, but I do not know how to use it. Can anybody help? Thank you very much for your time!

\$\endgroup\$
3
\$\begingroup\$

Since you are already requiring the filename to start with the correct drive name, your target will already look like a normal valid path. So you could at least check if what the user entered is actually the correct path, short-circuiting the whole search:

def search(drive, target):
    if os.path.isfile(target):
        return os.path.abspath(target)
    ...

You should also stop looking as soon as you have found your target and use the in keyword instead of manually checking all files yourself:

    ...
    for root, dirs, files in os.walk(r"D:\\"):
        if target in files:
            return os.path.abspath(os.path.join(root, target))

Other than that, I am afraid you are mostly out of luck. There are only two ways to quickly find something in a big pile of somethings:

  1. You search through all somethings until you find the something you are looking for.
  2. You have a datastructure, called an index, which tells you how to find each (or at least some, including hopefully the one something you are looking for) of the somethings.

In the Unix world there is the command line tool locate, which uses a periodically updated database of files to make finding files fast (as long as they are in that database). I am not sure what the equivalent Windows (command line) tool is, though you may find some solution here. You can then call that tool using subprocess.

Of yourse, if you need this search very often (twice is probably already often enough), you could also built such an index yourself, meaning you only have to check all files once:

from collections import defaultdict

INDEX = {}

def build_index(drive):
    index = defaultdict(set)
    for root, _, files in os.walk(drive):
        for file in files:
            index[file].add(root)
    return dict(index)

def search(drive, target):
    if drive not in INDEX:
        INDEX[drive] = build_index(drive)
    root = next(iter(INDEX[drive][target]))
    return os.path.abspath(os.path.join(root, target))

Note that this arbitrarily chooses one result if a file name appears multiple times, similarly to your code which chooses the last one encountered (with the order of processing arbitrarily defined). On my machine, for example, there are 8708 files called index.js and it is not clear which one you want. It might be a better interface to return all of them instead of one arbitrary one.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Great piece of code! I didn't think of this approach! I used your idea and it fits perfect in my code! Thank you so much for your kindly reply! I changed it a bit in the sense that after the search function is completed, the result will be stored in a repository from where my code can keep track of each file that had to be accessed at least once. Thank you very much! All the best! \$\endgroup\$
    – tudor.il
    Feb 9 at 20:31
  • 1
    \$\begingroup\$ locate ... I am not sure what the equivalent Windows (command line) tool is ... where is quite fast. \$\endgroup\$
    – ack
    Feb 9 at 22:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.