如何根据该行的 ID 从列表中删除该行?

时间:2021-05-30 12:37:21

标签: c# arrays list struct

过去几天我一直在努力解决这个问题,但我似乎无法让它发挥作用。

所以我有一个这样格式的txt文件:

id;könyvcím;szerző;kiadó;kiadási év;

我正在使用结构和列表,例如:

public static List<Books> BooksList = new List<Books>();

public struct Books
{
    public int id;
    public string title;
    public string writer;
    public string publisher;
    public int published_year;
}  

而且我还将所有这些都放入一个基于如下结构的列表中:

StreamReader booksRead = new StreamReader("konyvek.txt", Encoding.UTF8);
booksRead.ReadLine();
while (!booksRead.EndOfStream)
{
    string[] split = booksRead.ReadLine().Split(';');
    Books inRead = new Books();
    inRead.id = Convert.ToInt32(split[0]);
    inRead.title = split[1];
    inRead.writer = split[2];
    inRead.publisher = split[3];
    inRead.published_year = Convert.ToInt32(split[4]);
    BooksList.Add(inRead);
}
booksRead.Close();

例如,我只想找到 ID 为 2 的行的位置,然后从我的文本文件中删除该行。我试图获取我想要的行的索引,并像这样从我的文本文件中删除它,但它甚至无法获取索引,我尝试使用 IndexOf、FindIndex 并尝试进行循环。我很确定我的结构对我这样使用它不满意,因为我在运行代码时遇到这样的错误:

<块引用>

System.InvalidCastException: '无法将 'Books' 类型的对象转换为 输入“System.IConvertible”。

这是我尝试获取要删除的行的索引的方法

Books item = new Books();   
for (int i = 0; i < BooksList.Count; i++)
{
    if (Convert.ToInt32(textBox_id_delete.Text) == item.id)
    {
        RemoveAt = item.id;
    }
}
int index = BooksList.FindIndex(x => Convert.ToInt32(x) == RemoveAt);
MessageBox.Show(Convert.ToString(index));

我很确定我的做法非常错误,我愿意接受任何帮助。

5 个答案:

答案 0 :(得分:1)

出于多种原因,您完全错了。

首先,你会怎么做:

void Main()
{
    var filename = @"c:\myFolder\mybooklist.txt";
    // read into an enumerable
    var books = File.ReadAllLines(filename)
        .Select(x => x.Split(';'))
        .Select(x => new Book {
            Id = int.TryParse(x[0], out int bookId)?bookId:0,
            Title = x[1],
            Writer = x[2],
            Publisher = x[3],
            Published_year=int.TryParse(x[4], out int year)?year:0
        });
        
        
    // remove the one with id 2
    // and save back
    var otherBooks = books.Where(b => b.Id != 2);

    File.WriteAllLines(filename, otherBooks.Select(b => $"{b.Id};{b.Title};{b.Writer};{b.Publisher};{b.Published_year}"));
}

public struct Book
{
    public int Id;
    public string Title;
    public string Writer;
    public string Publisher;
    public int Published_year;
}

现在这有什么问题。

  1. 文本文件不是数据库,但您正尝试将文本文件用作数据库。
  2. 对于文本文件,您实际上并未在此处进行任何控制,无论 ID 是否唯一(可能有 N 本书的 ID 为 2)。
  3. (附带问题)您使用的是 C#,但看起来您来自另一种语言,并且根本不使用命名约定。

恕我直言,您应该简单地使用一个数据库,一个嵌入式数据库,例如 LiteDb 或 Sqlite。如果您想查看 LiteDb 或 Sqlite 示例,请告诉我。

编辑:我正在添加 SQLite 和 LiteDb 示例。无论哪种情况,您都需要分别从 Nuget 添加 Sqlite.Data.Sqlite 和 LiteDB 并添加 using 语句。

如果是 SQLite,请注意您可以使用 Linq 添加一些驱动程序。我直接使用了 ADO.Net 命令,没有使用 Book 类进行映射。

LiteDB 是一个用 C# 为 C# 编写的 NoSQL 数据库,可以直接使用对象并支持 Linq 开箱即用。

样品仅显示两者的表面。

答案 1 :(得分:1)

SQLite 示例:

private static readonly string dataFile = @"d:\temp\books.s3db";
void Main()
{
    CreateDb(dataFile);
    SeedSampleData(dataFile);
    // List the current data
    Console.WriteLine("Current Data");
    Console.WriteLine("".PadRight(100, '='));
    ListData(dataFile);
    Console.WriteLine("".PadRight(100, '='));
    DeleteSampleRow(dataFile);
    // List the current data
    Console.WriteLine("After deleting");
    Console.WriteLine("".PadRight(100, '='));
    ListData(dataFile);
    Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
    string deleteById = "delete from books where id = @id";
    string deleteByTitle = "delete from books where Title = @title";
    string deleteByWriter = "delete from books where Writer = @writer";


    using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
    using (SQLiteCommand cmdById = new SQLiteCommand(deleteById, cn))
    using (SQLiteCommand cmdByTitle = new SQLiteCommand(deleteByTitle, cn))
    using (SQLiteCommand cmdByWriter = new SQLiteCommand(deleteByWriter, cn))
    {
        cmdById.Parameters.Add("@id", DbType.Int32).Value = 2; // delete the book with id = 2
        cmdByTitle.Parameters.Add("@title", DbType.String).Value = $"Sample Title #5"; // delete all books having title "Sample Title #5"
        cmdByWriter.Parameters.Add("@writer", DbType.String).Value = $"Sample Writer #3"; // delete all books written by "Sample Writer #3"

        cn.Open();
        cmdById.ExecuteNonQuery();
        cmdByTitle.ExecuteNonQuery();
        cmdByWriter.ExecuteNonQuery();
        cn.Close();
    }
}

void ListData(string dbName)
{
    string selectCommand = "select * from books";
    using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
    using (SQLiteCommand cmd = new SQLiteCommand(selectCommand, cn))
    {
        cn.Open();
        var r = cmd.ExecuteReader();
        while (r.Read())
        {
            Console.WriteLine($"{r["id"]},{r["title"]},{r["writer"]},{r["publisher"]},{r["published_year"]}");
        }
        cn.Close();
    }
}

private void CreateDb(string dbName)
{
    if (File.Exists(dbName)) // if it exists, delete and create afresh, just for sampling
    {
        File.Delete(dbName);
    }
    string createTable = @"Create Table books (
            id int primary key not null, 
            title varchar(500) not null,
            writer varchar(100) not null,
            publisher varchar(100) not null,
            published_year int not null
            )";
    using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
    using (SQLiteCommand cmd = new SQLiteCommand(createTable, cn))
    {
        cn.Open();
        cmd.ExecuteNonQuery();
        cn.Close();
    }
}
private void SeedSampleData(string dbName)
{
    string insertCommand = @"insert into books 
        (id, title, writer, publisher, published_year)
        values
        (@id, @title, @writer, @publisher, @year);";
    using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
    using (SQLiteCommand cmd = new SQLiteCommand(insertCommand, cn))
    {
        cmd.Parameters.Add("@id", DbType.Int32);
        cmd.Parameters.Add("@title", DbType.String);
        cmd.Parameters.Add("@writer", DbType.String);
        cmd.Parameters.Add("@publisher", DbType.String);
        cmd.Parameters.Add("@year", DbType.Int32);
        Random r = new Random();
        cn.Open();

        int id = 1;
        using (SQLiteTransaction transaction = cn.BeginTransaction())
        {
            cmd.Parameters["@id"].Value = id++;
            cmd.Parameters["@title"].Value = $"Around the World in Eighty Days";
            cmd.Parameters["@writer"].Value = $"Jules Verne";
            cmd.Parameters["@publisher"].Value = $"Le Temps, Pierre-Jules Hetzel";
            cmd.Parameters["@year"].Value = 1873;
            cmd.ExecuteNonQuery();
            cmd.Parameters["@id"].Value = id++;
            cmd.Parameters["@title"].Value = $"A Tale of Two Cities";
            cmd.Parameters["@writer"].Value = $"Charles Dickens";
            cmd.Parameters["@publisher"].Value = $"Chapman & Hall";
            cmd.Parameters["@year"].Value = 1859;
            cmd.ExecuteNonQuery();


            // add dummy 10 more rows
            for (int i = 0; i < 10; i++)
            {
                cmd.Parameters["@id"].Value = id++;
                cmd.Parameters["@title"].Value = $"Sample Title #{i}";
                cmd.Parameters["@writer"].Value = $"Sample Writer #{r.Next(1, 5)}";
                cmd.Parameters["@publisher"].Value = $"Sample Publisher #{i}";
                cmd.Parameters["@year"].Value = r.Next(1980, 2022);
                cmd.ExecuteNonQuery();
            }
            transaction.Commit();
        }
        // databases generally use some indexes
        new SQLiteCommand(@"Create Index if not exists ixId on books (id);", cn).ExecuteNonQuery();
        new SQLiteCommand(@"Create Index if not exists ixTitle on books (title);", cn).ExecuteNonQuery();
        new SQLiteCommand(@"Create Index if not exists ixWriter on books (writer);", cn).ExecuteNonQuery();
        new SQLiteCommand(@"Create Index if not exists ixPublisher on books (publisher);", cn).ExecuteNonQuery();

        cn.Close();
    }
}

答案 2 :(得分:0)

当您将图书从文件放入列表时,您可以搜索该图书以从 BooksList 中删除。
删除它并将 BooksList 保存到文件中。

var removeBook = BookList.FirstOrDefault(book => book.id == removeId);
if (removeBook != null)
{
   BookList.Remove(removeBook);
}

var booksAsString = BookList.Select(book => $"{book.id};{book.title};{book.writer};{book.publisher};{book.published_year}");

File.WriteAllLines("konyvek.txt", booksAsString, Encoding.UTF8);

答案 3 :(得分:0)

LiteDb sample:

private static readonly string dataFile = @"d:\temp\books.litedb";

void Main()
{
    //CreateDb(dataFile); // this step is not needed with LiteDB
    // instead we just simply delete the datafile if it exists 
    // for starting afresh
    // if it exists, delete and create afresh, just for sampling
    // so you can run this same sample over and over if you wish
    if (File.Exists(dataFile)) 
    {
        File.Delete(dataFile);
    }

    SeedSampleData(dataFile);
    // List the current data
    Console.WriteLine("Current Data");
    Console.WriteLine("".PadRight(100, '='));
    ListData(dataFile);
    Console.WriteLine("".PadRight(100, '='));
    DeleteSampleRow(dataFile);
    // List the current data
    Console.WriteLine("After deleting");
    Console.WriteLine("".PadRight(100, '='));
    ListData(dataFile);
    Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
    using (var db = new LiteDatabase(dbName))
    {
        var bookCollection = db.GetCollection<Book>("Books");
        // by ID
        bookCollection.Delete(2);
        // by Title
        bookCollection.DeleteMany(c => c.Title == "Sample Title #5");
        // by Writer
        bookCollection.DeleteMany(c => c.Writer == "Sample Writer #3");
    }
}

void ListData(string dbName)
{
    using (var db = new LiteDatabase(dbName))
    {
        var bookCollection = db.GetCollection<Book>("Books");
        foreach (var book in bookCollection.FindAll())
        {
            Console.WriteLine($"{book.Id},{book.Title},{book.Writer},{book.Publisher},{book.Published_year}");
        }
    }
}

private void SeedSampleData(string dbName)
{
    Random r = new Random();
    var books = new List<Book> {
            new Book {Title="Around the World in Eighty Days",Writer = "Jules Verne",Publisher = "Le Temps, Pierre-Jules Hetzel",Published_year= 1873},
            new Book {Title="A Tale of Two Cities",Writer = "Charles Dickens",Publisher = "Chapman & Hall",Published_year= 1859},
        };
    // add dummy 10 more rows
    books.AddRange(Enumerable.Range(0, 10).Select(i => new Book
    {
        Title = $"Sample Title #{i}",
        Writer = $"Sample Writer #{r.Next(1, 5)}",
        Publisher = $"Sample Publisher #{i}",
        Published_year = r.Next(1980, 2022)
    }));
    using (var db = new LiteDatabase(dbName))
    {
        var bookCollection = db.GetCollection<Book>("Books");
        bookCollection.InsertBulk(books);

        // databases generally use some indexes
        // create the same indexes that we created in SQLite sample
        bookCollection.EnsureIndex(c => c.Id);
        bookCollection.EnsureIndex(c => c.Title);
        bookCollection.EnsureIndex(c => c.Writer);
        bookCollection.EnsureIndex(c => c.Publisher);
    }
}

public class Book
{
    public int Id {get;set;}
    public string Title {get;set;}
    public string Writer {get;set;}
    public string Publisher {get;set;}
    public int Published_year {get;set;}
}

答案 4 :(得分:0)

欢迎来到 SO。我将假设您有理由将数据保存在文本文件中。正如几个答案所建议的那样,如果您需要在文本文件中使用它,最简单的方法就是使用您想要的行创建一个新文件。

一种方法是使用 interator 函数来过滤行。这使您可以轻松地使用 .NET File 类来完成其余的工作 - 创建新文件并根据需要删除旧文件。通常保留旧文件并将其存档也很有用,但无论如何,这里有一种过滤行的方法。

    static void Main(string[] _)
    {
        var filteredLines = FilterOnID(File.ReadAllLines("datafile.txt"), "2");

        File.WriteAllLines("updated.datafile.txt", filteredLines);

        // rename if necessary
        File.Delete("datafile.txt");
        File.Move("updated.datafile.txt", "datafile.txt");
    }

    static IEnumerable<string> FilterOnID(IEnumerable<string> lines, string id)
    {
        foreach (var line in lines)
        {
            var fields = line.Split(';');

            if (fields.Length != 0 || !string.IsNullOrEmpty(fields[0]))
            {
                if (id == fields[0])
                    continue;
            }

            yield return line;
        }
    }

为了测试,我添加了这样的简单文件:

1;field1;field2;field3
2;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3

运行后你会得到这个:

1;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3