为什么for / while循环意外删除字符串?

时间:2012-07-08 21:30:03

标签: python

我无法弄清楚为什么当用户输入'no'时第一个包含while的循环有效以及为什么第二个实例不起作用。

我相信两个循环的构造几乎完全相同。第二次出现只是分解成另一种功能。

当用户输入'no'时,它不应该删除字符串。

这个有效。如果用户输入“否”,则不会删除任何内容。

def remove():
    f = open('codilist.txt')
    coname = raw_input('What company do you want to remove? ') # company name
    tmpfile = open('codilist.tmp', 'w')
    for line in f:
        if coname.upper() in line:
            while True:
                answer = raw_input('Are you sure you want to remove company?\nyes or no?')
                if answer == 'yes':
                    print line.upper() + '...has been removed.'
                    break                
                elif answer == 'no':
                    f.close()
                    tmpfile.close()
                    return
                else:
                    print 'Please choose yes or no.'                    
        else:
            tmpfile.write(line)
    f.close()
    tmpfile.close()
    os.rename('codilist.tmp', 'codilist.txt')

这个不起作用。如果用户输入'no',它会删除字符串。

def find_and_remove(f,coname,tmpfile):
    for line in f:
        if coname.upper() in line:
            while True:
                answer = raw_input('Are you sure you want to remove company?\nyes or no?')
                if answer == 'yes':
                    print line.upper() + '...has been removed.'
                    break               
                elif answer == 'no':
                    f.close()
                    tmpfile.close()
                    return
                else:
                    print 'Please choose yes or no.'                   
        else:
            tmpfile.write(line)

def remove():
    f = open('codilist.txt')
    coname = raw_input('What company do you want to remove? ') # company name
    tmpfile = open('codilist.tmp', 'w')
    find_and_remove(f,coname,tmpfile)
    f.close()
    tmpfile.close()
    os.rename('codilist.tmp', 'codilist.txt')

5 个答案:

答案 0 :(得分:3)

为了展示更好的编码风格,我冒昧地重写下面的脚本:

def get_user_in(message, valid_responses):
    while True:
        user_in = raw_input(message)
        if user_in in valid_responses:
            return user_in
        else:
            print "Please choose from: {0} or {1}".format(*valid_responses)

def find_and_remove(co_name, infile, outfile):
    pattern = co_name.upper()
    for line in infile:
        if pattern in line.upper():
            print line
            answer = get_user_in("Remove this line? ", ("yes", "no"))
            if answer == "no":
                outfile.write(line)
        else:
            outfile.write(line)

def remove(filename):
    outFilename = filename + '.tmp'
    with open(filename, 'r') as infile, open(outFilename, 'w') as tmpfile:
        co_name = raw_input('What company do you want to remove? ')
        find_and_remove(co_name, infile, tmpfile)
    os.rename(outFilename, filename)

答案 1 :(得分:2)

请参阅Joel's wiki answer,了解如何重做这一切的好例子。

但至于修复错误......

我终于意识到你的两个例子之间的差别很大。它与您执行rename

时有关

在您的第一个示例中,当用户说“不”时,从整个功能返回会阻止您os.rename发生任何事情。结果是您看到一个完全不变的原始.txt文件。

在你的第二个例子中,如果用户说“不”,你将从子功能返回到主功能,但os.rename无论发生什么。这意味着如果他们说不,你不再写任何行,但你仍然复制了一半处理过的tmp文件。

虽然老实说我认为应该重写整个内容,但是对当前代码的一个简单调整将是find_and_remove返回进程是否成功,可能是bool:

def find_and_remove(f,coname,tmpfile):
    ...             
                elif answer == 'no':
                    return False
    return True

def remove():
    ...
    success = find_and_remove(f,coname,tmpfile)
    f.close()
    tmpfile.close()
    if success:
        os.rename('codilist.tmp', 'codilist.txt')

老实说,您应该考虑使用with上下文来打开和关闭文件,这样您就不必混淆何时关闭文件的条件。你可以用两种不同的功能关闭它们。

with声明

with open('infile.txt') as inFile, open('outfile.txt', 'w') as outFile:
    for line in inFile:
        outFile.write(line)

with块结束时,它会关闭文件。

答案 2 :(得分:1)

在我看来,问题是“no”子句中的return。这在tmpfile.write(line)方法执行之前退出函数,因此对我来说看起来像字符串被删除是有意义的。我不明白为什么它会在第一个函数中起作用。

答案 3 :(得分:0)

在第二种情况下,当用户键入find_and_remove时,您只会退出no。在第一种情况下,您将退出整个函数。

答案 4 :(得分:0)

我和Joel有同样的想法,但有点慢......无论如何,希望这是有意义的:

COMPANY_FILE = 'codilist.txt'

def load_companies():
    with open(COMPANY_FILE) as inf:
        return filter(None, (line.strip() for line in inf))

def save_companies(companies):
    with open(COMPANY_FILE, 'w') as outf:
        outf.write('\n'.join(companies))

def get_option(prompt, options):
    options = [opt.strip().lower() for opt in options]
    prompt = "{}? [{}]".format(prompt, '/'.join(options))
    options = set(options)
    while True:
        res = raw_input(prompt).strip().lower()
        if res in options:
            return res

def remove_company(remove, companies):
    ask = lambda c: get_option('Remove company {}'.format(c), ['yes', 'no'])
    return [co for co in companies if remove not in co or ask(co)=='no']

def main():
    companies = load_companies()
    co_len = len(companies)

    remove = raw_input('Name of company to remove?').strip()
    companies = remove_company(remove, companies)

    if len(companies) < co_len:
        save_companies(companies)

if __name__=="__main__":
    main()