Python: проверить, существует ли файл из списка, выполнять функцию, только если он существует - PullRequest
1 голос
/ 16 октября 2011

Python Noob ... пожалуйста, будьте нежны. В моей текущей программе у меня есть список из 3 файлов, которые могут находиться или не находиться в моем текущем каталоге. Если они находятся в моем каталоге, я хочу иметь возможность назначать им значения для последующего использования в других функциях. Если файл не находится в каталоге, ему не следует присваивать значения, поскольку файл все равно не существует. Код, который у меня есть, приведен ниже:

import os, csv

def chkifexists():
    files = ['A.csv', 'B.csv', 'C.csv']
    for fname in files:
        if os.path.isfile(fname):
            if fname == "A.csv":
                hashcolumn = 7
                filepathNum = 5
            elif fname == "B.csv":
                hashcolumn = 15
                filepathNum = 5
            elif fname == "C.csv":
                hashcolumn = 1
                filepathNum = 0
        return fname, hashcolumn, filepathNum


def removedupes(infile, outfile, hashcolumn):
    fname, hashcolumn, filepathNum = chkifexists()
    r1 = file(infile, 'rb')
    r2 = csv.reader(r1)
    w1 = file(outfile, 'wb')
    w2 = csv.writer(w1)
    hashes = set()
    for row in r2:
        if row[hashcolumn] =="": 
            w2.writerow(row)       
            hashes.add(row[hashcolumn])  
        if row[hashcolumn] not in hashes:
            w2.writerow(row)
            hashes.add(row[hashcolumn])
    w1.close()
    r1.close()


def bakcount(origfile1, origfile2):
    '''This function creates a .bak file of the original and does a row count to determine
    the number of rows removed'''
    os.rename(origfile1, origfile1+".bak")
    count1 = len(open(origfile1+".bak").readlines())
    #print count1

    os.rename(origfile2, origfile1)
    count2 = len(open(origfile1).readlines())
    #print count2

    print str(count1 - count2) + " duplicate rows removed from " + str(origfile1) +"!"


def CleanAndPrettify():
    print "Removing duplicate rows from input files..."
    fname, hashcolumn, filepathNum = chkifexists()
    removedupes(fname, os.path.splitext(fname)[0] + "2.csv", hashcolumn)
    bakcount (fname, os.path.splitext(fname)[0] + "2.csv")


CleanAndPrettify()

Проблема, с которой я сталкиваюсь, заключается в том, что код проходит по списку и останавливается на первом действительном найденном файле.

Я не уверен, полностью ли я думаю об этом, но подумал, что все делаю правильно.

Текущий вывод этой программы с A.csv, B.csv и C.csv, находящимися в одном каталоге:

Removing duplicate rows from input files...
2 duplicate rows removed from A.csv!

Желаемый выход должен быть:

Removing duplicate rows from input files...
2 duplicate rows removed from A.csv!
5 duplicate rows removed from B.csv!
8 duplicate rows removed from C.csv!

... и затем переходите к следующей части создания файлов .bak. Вывод этой программы без любых файлов CSV в том же каталоге:

UnboundLocalError: local variable 'hashcolumn' referenced before assignment

Ответы [ 3 ]

2 голосов
/ 16 октября 2011

Условие проверки, которое вы используете, не является предлагаемым способом сравнения двух строк в python. Если вы явно не интернируете строку, вы не должны использовать is для сравнения, поскольку нет гарантии, что она вернет True используйте == вместо.

В качестве альтернативы вы можете сделать следующее:

files=['A.csv', 'B.csv', 'C.csv']
filedict['A.csv']=(7,5)
filedict['B.csv']=(15,5)
filedict['C.csv']=(1,0)
print [(fname,filedict[fname]) for fname in files if filedict.has_key(fname) and os.path.isfile(fname)]
2 голосов
/ 16 октября 2011

Конечно, он останавливается после первого совпадения, потому что вы делаете return из функции.Вместо этого вы должны либо заполнить некоторый массив в цикле и return его в конце, либо создать генератор, используя yield на каждой итерации и raise StopIteration в случае, если ничего не найдено.Первый подход проще и ближе к вашему решению, вот он:

import os, csv

def chkifexists():
    files = ['A.csv', 'B.csv', 'C.csv']
    found = []
    for fname in files:
        if os.path.isfile(fname):
            if fname == "A.csv":
                hashcolumn = 7
                filepathNum = 5
            elif fname == "B.csv":
                hashcolumn = 15
                filepathNum = 5
            elif fname == "C.csv":
                hashcolumn = 1
                filepathNum = 0
            found.append({'fname': fname,
                          'hashcolumn': hashcolumn,
                          'filepathNum': filepathNum})
    return found

found = chkifexists()
if not found:
    print 'No files to scan'
else
    for f in found:
        print f['fname'], f['hashcolumn'], f['filepathNum']
1 голос
/ 20 октября 2011

У вас есть пара проблем в вашем коде.

Сначала chkifexists начинает return, как только находит существующий файл, поэтому никогда не проверяет оставшиеся имена; Кроме того, если файлы не найдены, то hashcolumn и filepathNum никогда не устанавливаются, что дает вам UnboundLocalError.

Во-вторых, вы звоните chkifexists в двух местах - с removedupes и с CleanAndPrettify. Так что removedupes будет работать для каждого существующего файла для каждого существующего файла - не то, что вы хотите! Фактически, поскольку CleanAndPrettify только что проверил, что файл существует, removedupes должен просто идти с тем, что ему передано.

Существует как минимум три способа обработки случая, когда файлы не найдены: chkifexists вызвать исключение; в CleanAndPrettify есть флаг, который отслеживает, были ли найдены файлы; или превратите результаты chkifexists в list, которые затем можно проверить на пустоту.

В измененном коде я переместил файлы в словарь с именем в качестве ключа и значением в виде кортежа hashcolumn и filepathNum. chkifexists теперь принимает имена файлов для поиска в качестве словаря и yield s значения, когда файл найден; если файлы не найдены, будет сгенерировано исключение NoFilesFound.

Вот код:

import os, csv

# store file attributes for easy modifications
# format is 'filename': (hashcolumn, filepathNum)
files = {
        'A.csv': (7, 5),
        'B.csv': (15, 5),
        'C.csv': (1, 0),
        }

class NoFilesFound(Exception):
    "No .csv files were found to clean up"

def chkifexists(somefiles):
    # load all three at once, but only yield them if filename
    # is found
    filesfound = False
    for fname, (hashcolumn, filepathNum) in somefiles.items():
        if os.path.isfile(fname):
            filesfound = True
            yield fname, hashcolumn, filepathNum
    if not filesfound:
        raise NoFilesFound

def removedupes(infile, outfile, hashcolumn, filepathNum):
    # this is now a single-run function
    r1 = file(infile, 'rb')
    r2 = csv.reader(r1)
    w1 = file(outfile, 'wb')
    w2 = csv.writer(w1)
    hashes = set()
    for row in r2:
        if row[hashcolumn] =="": 
            w2.writerow(row)       
            hashes.add(row[hashcolumn])  
        if row[hashcolumn] not in hashes:
            w2.writerow(row)
            hashes.add(row[hashcolumn])
    w1.close()
    r1.close()


def bakcount(origfile1, origfile2):
    '''This function creates a .bak file of the original and does a row count
    to determine the number of rows removed'''
    os.rename(origfile1, origfile1+".bak")
    count1 = len(open(origfile1+".bak").readlines())
    #print count1

    os.rename(origfile2, origfile1)
    count2 = len(open(origfile1).readlines())
    #print count2

    print str(count1 - count2) + " duplicate rows removed from " \
        + str(origfile1) +"!"


def CleanAndPrettify():
    print "Removing duplicate rows from input files..."
    try:
        for fname, hashcolumn, filepathNum in chkifexists(files):
            removedupes(
                   fname,
                   os.path.splitext(fname)[0] + "2.csv",
                   hashcolumn,
                   filepathNum,
                   )
            bakcount (fname, os.path.splitext(fname)[0] + "2.csv")
    except NoFilesFound:
        print "no files to clean up"

CleanAndPrettify()

Невозможно проверить, так как у меня нет файлов A, B и C .csv, но, надеюсь, это укажет вам правильное направление. Как видите, опция raise NoFilesFound использует метод flag для отслеживания файлов, которые не были найдены; вот метод list:

def chkifexists(somefiles):
    # load all three at once, but only yield them if filename
    # is found
    for fname, (hashcolumn, filepathNum) in somefiles.items():
        if os.path.isfile(fname):
            filesfound = True
            yield fname, hashcolumn, filepathNum

def CleanAndPrettify():
    print "Removing duplicate rows from input files..."
    found_files = list(chkifexists(files))
    if not found_files:
        print "no files to clean up"
    else:
        for fname, hashcolumn, filepathNum in found_files:
            removedupes(...)
            bakcount(...)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...