Python - 如何为用户输入创建删除函数?

Posted

技术标签:

【中文标题】Python - 如何为用户输入创建删除函数?【英文标题】:Python - How to create a delete function for user input? 【发布时间】:2020-12-05 14:52:40 【问题描述】:

我一直在尝试创建一个程序,允许用户查看文本文件的内容并删除部分或全部单个条目块。

文本文件内容的示例如下所示:

Special Type A Sunflower
2016-10-12 18:10:40
Asteraceae
Ingredient in Sunflower Oil
Brought to North America by Europeans
Requires fertile and moist soil
Full sun

Pine Tree
2018-12-15 13:30:45
Pinaceae
Evergreen
Tall and long-lived
Temperate climate

Tropical Sealion
2019-01-20 12:10:05
Otariidae
Found in zoos
Likes fish
Likes balls
Likes zookeepers

Big Honey Badger
2015-06-06 10:10:25
Mustelidae
Eats anything
King of the desert

因此,入口块是指所有没有水平空格的行。

目前,我的进度是:

import time
import os
global o
global dataset
global database
from datetime import datetime

MyFilePath = os.getcwd() 
ActualFile = "creatures.txt"
FinalFilePath = os.path.join(MyFilePath, ActualFile) 

def get_dataset():
    database = []
    shown_info = []
    with open(FinalFilePath, "r") as textfile:  
        sections = textfile.read().split("\n\n")
        for section in sections:                 
            lines = section.split("\n")      
            database.append(                
              "Name": lines[0],
              "Date": lines[1],              
              "Information": lines[2:]          
            )
    return database

def delete_creature():
    dataset = get_dataset()
    
    delete_question = str(input("Would you like to 1) delete a creature or 2) only some of its information from the dataset or 3) return to main page? Enter 1, 2 or 3: "))
    
    if delete_question == "1":
        delete_answer = str(input("Enter the name of the creature: "))
        for line in textfile:
            if delete_answer in line:
                line.clear()
            
    elif delete_question == "2":
        delete_answer = str(input("Enter the relevant information of the creature: "))
        for line in textfile:
            if delete_answer in line:
                line.clear()
        
    elif delete_question == "3":
        break
        
    else:
        raise ValueError
    except ValueError:
        print("\nPlease try again! Your entry is invalid!")
        
while True:
    try:
        option = str(input("\nGood day, This is a program to save and view creature details.\n" +
                       "1) View all creatures.\n" +
                       "2) Delete a creature.\n" +
                       "3) Close the program.\n" +
                       "Please select from the above options: "))
        
        if option == "1":
            view_all()
            
        elif option == "2":
            delete()
                
        elif option == "3":
            break
            
        else:
            print("\nPlease input one of the options 1, 2 or 3.")
        
    except:
        break

delete_function() 旨在通过以下方式删除生物:

    名称,删除与名称关联的整个文本块 Information,只删除一行信息

但是,我似乎无法让 delete_creature() 函数工作,而且我不确定如何让它工作。

有人知道如何让它工作吗?

非常感谢!

【问题讨论】:

我这样做的方法是逐行复制文件,如果您打算删除它,则不要添加它。不过可能有更好的方法。 这能回答你的问题吗? using Python for deleting a specific line in a file @PatrickArtner:OP 已经有一个 get_dataset 函数,可以将文件解析为 dict 列表。恐怕问题不在于(仅)从文件中删除一行 - 删除整个部分没有意义...... 文件使用文件指针。您不能在不重新加载文件的情况下多次读取相同的行。 with() 之类的上下文处理程序也会关闭文件-因此您的代码会在您的删除函数内的for line in textfile: 上产生错误...这似乎根本不起作用...参见why-can-i-read-lines-from-file-only-one-time-本质上也是如此:您的删除函数“提示”删除和更新应该是不同的函数并且应该在解析的列表上工作 - 而不是文件 看起来好像有人写了一个框架(数据解析),没有人负责删除/更新的任务。这是部分给定代码的作业吗? 【参考方案1】:

您从部分中删除行的问题是您专门硬编码了哪一行代表什么。在您的情况下删除一个部分将很容易,如果您不改变您的概念,删除一行将涉及将相关行设置为空或稍后表示空字符串的某个字符。 这里的另一个问题是,您是否需要您的部分在输入时保持有序,或者您可以让它们以其他顺序在文件中重新排序。

我要做的是将输入文件格式更改为例如INI 文件格式。然后您可以使用 configparser 模块以简单的方式解析和编辑它们。 INI 文件如下所示:

[plant1]
name="Some plant's English name"
species="The plant's Latin species part"
subspecies="The plant's subspecies in Latin ofcourse"
genus="etc."

[animal1]
# Same as above for the animal
# etc. etc. etc.

configparser.ConfigParser() 将让您以字典方式加载它并编辑部分和值。您可以将部分命名为 animal1、plant1 或将它们用作其他名称,但我更喜欢将名称保留在值中,通常在 name 键下,然后使用 configparser 从名称创建一个普通字典,其中它的值是另一个包含节中指定的键值对。我在保存结果时反转了这个过程。手动或再次使用 configparser。

您可能考虑的另一种格式是 JSON,使用 json 模块。 使用其函数 dumps() 并正确设置分隔符和缩进,您将获得非常易于阅读和可编辑的输出格式。好消息是您保存了正在使用的数据结构,例如字典,然后你加载它,它会在你保存它时返回,你不需要执行一些额外的东西来完成它,就像使用 configparser。问题是,对于不习惯于 JSON 构造的用户来说,INI 文件不会那么混乱,并且导致错误更少,而 JSON 必须严格格式化,并且在打开和关闭范围或使用分隔符时出现任何错误都会导致整个事情不起作用或输入不正确。当文件很大时很容易发生这种情况。

这两种格式都允许用户将空行放在任何他们想要的地方,并且它们不会改变文件的加载方式,而您的方法对空行是严格的。

如果您希望您的数据库只能由您的程序编辑,那么请使用 pickle 模块来执行此操作并为自己省去麻烦。

否则你可以:

def getdata (stringfromfile):
    end = 
    l = [] # lines in a section
    for x in stringfromfile.strip().splitlines():
        x = x.strip()
        if not x: # New section encountered
            end[l[0].lower()] = l[1:]
            l = []
            continue
        end.append(x)
    end[l[0].lower()] = l[1:] # Add last section
    # Connect keys to numbers in the same dict(), so that users can choose by number too
    for n, key in enumerate(sorted(end)):
        end[n] = key
    return end

# You define some constants for which line is what in a dict():
values = "species": 0, "subspecies": 1, "genus": 2

# You load the file and parse the data
data = getdata(f.read())

def edit  (name_or_number, edit_what, new_value):
    if isinstance(name_or_number, int):
        key = data[name_or_number]
    else:
        key = name_or_number.lower().strip()
    if isinstance(edit_what, str):
        edit_what = values[edit_what.strip().lower()]
    data[key][edit_what] = new_value.strip()

def add (name, list_of_lines):
    n = len(data)/2 # Number for new entry for numeric getting
    name = name.strip().lower()
    data[name] = list_of_lines
    data[n] = name

def remove (name):
    name = name.lower().strip()
    del data[name]
    # Well, this part is bad and clumsy
    # It would make more sense to keep numeric mappings in separate list
    # which will do this automatically, especially if the database file is big-big-big...
    # But I started this way, so, keeping it simple and stupid, just remap everything after removing the item (inefficient as hell itself)
    for x in data.keys():
        if isinstance(x, int):
            del data[x]
    for n, key in enumerate(sorted(data)):
        data[n] = key

def getstring (d):
    # Serialize for saving
    end = []
    for l0, ls in d.items():
        if isinstance(l0, int):
            continue # Skip numeric mappings
        lines = l0+"\n"+"\n".join(ls)
        end.append(lines)
    return "\n\n".join(end)

我没有测试代码。可能有错误。

如果您不需要特定的行,您可以轻松地修改我的代码以使用 list.index() 方法在行中搜索,或者在需要获取它们时仅使用数字作为行。要使用 configparser 执行此操作,请在以下部分中使用通用键:answer0、answer1... 或仅 0、1、2...,然后忽略它们并将答案加载为列表或但是。如果您打算使用 configparser 处理文件,有时会在删除时得到 answer0、answer3...。

还有一个警告。如果你想保持输入文件给出生物的顺序,请使用ordereddict而不是普通字典。

此外,在原地编辑打开的文件当然是可能的,但复杂且不可取,所以不要这样做。加载并保存回来。当您想直接更改文件时,这种情况很少见。为此,您将使用 mmap 模块。只是不要!

【讨论】:

感谢您的帮助!这就是我要找的东西!

以上是关于Python - 如何为用户输入创建删除函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何为用户输入文本时将动态创建的每个单元格设置按钮操作(Swift 4)?

如何为 TableWidget 创建视觉辅助工具

如何为“其他 - 请指定”制作单选按钮?

Python:如何为 GUI 类创建单独的模块

如何为用户输入的键调用字典?

如何为 python 3.7.0 创建虚拟环境?