3

我有一个字符串块文件,每个都以某个关键字结尾。我目前有一个流阅读器设置,它将文件的每一行添加到列表中,直到当前块的末尾(行包含指示块结束的关键字)。

listName.Add(lineFromFile);

每个块都包含信息,例如 Book BookName、Author AuthorName、Journal JournalName 等。所以每个块假设是一个单独的项目(书籍、期刊、会议等)。

现在有大约 50 个左右的信息块(项目),我需要一些方法来存储信息,以便我可以操纵它并存储每个作者、标题、页面等,并知道哪些信息与哪些项目等有关。

在键入此内容时,我想出了可能将每个项目存储为名为“项目”的类的对象的想法,但是可能有几个作者,我不确定如何实现这一点,因为我在想可能使用命名变量的计数器,例如

int i = 0;
String Author[i] = "blahblah";
i++;

但据我所知,这是不允许的?所以我的问题基本上是存储每个项目的最简单/最简单的方法是什么,以便我可以操纵字符串来存储每个项目以供以后使用。

@yamen 这是该文件的一个示例:

Author Bond, james
Author Smith John A
Year 1994
Title For beginners
Book Accounting
Editor Smith Joe
Editor Doe John
Publisher The University of Chicago Press
City Florida, USA
Pages 15-23
End

Author Faux, M
Author Sedge, M
Author McDreamy, L
Author Simbha, D
Year 2000
Title Medical advances in the modern world
Journal Canadian Journal of medicine
Volume 25
Pages 1-26
Issue 2
End


Author McFadden, B
Author Goodrem, G
Title Shape shifting dinosaurs
Conference Ted Vancouver
City Vancouver, Canada
Year 2012
Pages 2-6
End
4

8 回答 8

4

更新代替您的样品

如何解析字符串超出了这个答案的范围 - 你可能想自己尝试一下,然后问另一个 SO(我建议阅读 SO 的黄金法则:https ://meta.stackexchange.com/questions /128548/what-stack-overflow-is-not)。

因此,我将提出解决方案,假设您有一个字符串表示完整的书籍/期刊信息块(此数据看起来像引文)。我原来的答案的主要变化是你有多个作者。此外,您可能需要考虑是否要将作者的姓名转换回[first name/initial] [middle names] [surname].

我提出了两种解决方案——一种是 using Dictionary,一种是 using Linq。Linq 解决方案是单线的。

定义一个Info类来存储项目:

public class Info
{
   public string Title { get; private set; }
   public string BookOrJournal { get; private set; }
   public IEnumerable<string> Authors { get; private set; }
   //more members of pages, year etc.
   public Info(string stringFromFile)
   {
     Title = /*read book name from stringFromFile */;
     BookOrJournalName = /*read journal name from stringFromFile */;
     Authors = /*read authors from stringFromFile */;
   }
}

请注意,stringFromFile引用信息应该是一个块,包括换行符。

现在一个字典来按作者存储每个信息:

Dictionary<string, List<Info>> infoByAuthor = 
  new Dictionary<string, List<Info>>(StringComparer.OrdinalIrgnoreCase);

注意OrdinalIgnoreCase比较器 - 处理作者姓名以不同大小写打印的情况。

给定List<string>您要添加的a listName.Add,这个简单的循环就可以解决问题:

List<Info> tempList;
Info tempInfo;
foreach(var line in listName)
{
  if(string.IsNullOrWhiteSpace(line))
    continue;
  tempInfo = new Info(line);
  foreach(var author in info.Authors)
  {
    if(!infoByAuthor.TryGetValue(author, out tempList))
      tempInfo[author] = tempList = new List<Info>();
    tempList.Add(tempInfo);
  }
}

现在您可以遍历字典,每个都KeyValuePair<string, List<Info>>将具有Key与作者姓名相同的值,并且将是具有该作者的对象Value列表。Info请注意,AuthorName即使您在不区分大小写的情况下分组,也会从文件中保留 的大小写,这样带有"jon skeet"和的两个项目"Jon Skeet"将被分组到同一个列表中,但它们的原始大小写将保留在Info.

此外,编写代码以确保Info每次引用只创建一个实例,出于多种原因(内存、集中更新等),这是更可取的。

或者,使用 Linq,您可以简单地执行以下操作:

var grouped = listName.Where(s => !string.IsNullOrWhiteSpace(s))
  .Select(s => new Info(s))
  .SelectMany(i => 
    s.Authors.Select(ia => new KeyValuePair<string, Info>(ia, i))
  .GroupBy(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);

现在您有了可枚举的组,其中Key是作者姓名,而内部可枚举是Info具有该作者姓名的所有对象。在这里也将观察到关于“两个飞碟”的相同的保留案例的行为。

于 2012-05-15T05:32:28.997 回答
2

您正在顺利地发明关系数据库。方便的是,这些已经可用。除了解决实体之间存储关系的问题外,它们还处理并发问题,并得到基于可证明数学的建模技术的支持。


解析器是他们自己的主题。由于 SQL 是不可能的,这是一个人为的大学作业,我确实有一些观察。

  • 简单的方法是使用正则表达式。然而,对于大型输入文件,这是非常低效的和糟糕的解决方案。
  • 在没有正则表达式的情况下,String.IndexOf() 和 String.Split() 是你的朋友。
  • 如果您的评估员无法处理 SQL,那么 LINQ 将会非常令人震惊,但我真的很喜欢 Zoltan 的 LINQ 解决方案,它非常优雅。
于 2012-05-15T05:49:25.200 回答
2

我会为此使用多值字典:

public struct BookInfo
    {
        public string Title;
        public string Journal;
    }

然后创建一个字典对象:

var dict = new Dictionary<Author, BookInfo>();

这样,如果您遇到多个作者,数据将按作者排序,这使得编写未来的代码来处理这些数据变得容易。打印出某个作者所有书籍的列表将非常容易,并且不需要繁琐的搜索过程。

于 2012-05-15T05:35:56.560 回答
2

这是此问题的完整代码。它是用一种简单、直接的方法编写的。它可以优化,没有错误检查,并且AddData可以通过使用反射以更有效的方式编写方法。但它以一种优雅的方式完成了这项工作。

using System;
using System.Collections.Generic;
using System.IO;

namespace MutiItemDict
{
    class MultiDict<TKey, TValue>  // no (collection) base class
    {
        private Dictionary<TKey, List<TValue>> _data = new Dictionary<TKey, List<TValue>>();

        public void Add(TKey k, TValue v)
        {
            // can be a optimized a little with TryGetValue, this is for clarity
            if (_data.ContainsKey(k))
                _data[k].Add(v);
            else
                _data.Add(k, new List<TValue>() { v });
        }

        public List<TValue> GetValues(TKey key)
        {
            if (_data.ContainsKey(key))
                return _data[key];
            else
                return new List<TValue>();
        }
    }

    class BookItem
    {
        public BookItem()
        {
            Authors = new List<string>();
            Editors = new List<string>();
        }

        public int? Year { get; set; }
        public string Title { get; set; }
        public string Book { get; set; }
        public List<string> Authors { get; private set; }
        public List<string> Editors { get; private set; }
        public string Publisher { get; set; }
        public string City { get; set; }
        public int? StartPage { get; set; }
        public int? EndPage { get; set; }
        public int? Issue { get; set; }
        public string Conference { get; set; }
        public string Journal { get; set; }
        public int? Volume { get; set; }

        internal void AddPropertyByText(string line)
        {
            string keyword = GetKeyWord(line);
            string data = GetData(line);
            AddData(keyword, data);
        }

        private void AddData(string keyword, string data)
        {
            if (keyword == null)
                return;

            // Map the Keywords to the properties (can be done in a more generic way by reflection)
            switch (keyword)
            {
                case "Year":
                    this.Year = int.Parse(data);
                    break;
                case "Title":
                    this.Title = data;
                    break;
                case "Book":
                    this.Book = data;
                    break;
                case "Author":
                    this.Authors.Add(data);
                    break;
                case "Editor":
                    this.Editors.Add(data);
                    break;
                case "Publisher":
                    this.Publisher = data;
                    break;
                case "City":
                    this.City = data;
                    break;
                case "Journal":
                    this.Journal = data;
                    break;
                case "Volume":
                    this.Volume = int.Parse(data);
                    break;
                case "Pages":
                    this.StartPage = GetStartPage(data);
                    this.EndPage = GetEndPage(data);
                    break;
                case "Issue":
                    this.Issue = int.Parse(data);
                    break;
                case "Conference":
                    this.Conference = data;
                    break;
            }
        }

        private int GetStartPage(string data)
        {
            string[] pages = data.Split('-');
            return int.Parse(pages[0]);
        }

        private int GetEndPage(string data)
        {
            string[] pages = data.Split('-');
            return int.Parse(pages[1]);
        }

        private string GetKeyWord(string line)
        {
            string[] words = line.Split(' ');
            if (words.Length == 0)
                return null;
            else
                return words[0];
        }

        private string GetData(string line)
        {
            string[] words = line.Split(' ');
            if (words.Length < 2)
                return null;
            else
                return line.Substring(words[0].Length+1);
        }
    }

    class Program
    {
        public static BookItem ReadBookItem(StreamReader streamReader)
        {
            string line = streamReader.ReadLine();
            if (line == null)
                return null;

            BookItem book = new BookItem();
            while (line != "End")
            {
                book.AddPropertyByText(line);
                line = streamReader.ReadLine();
            }
            return book;
        }

        public static List<BookItem> ReadBooks(string fileName)
        {
            List<BookItem> books = new List<BookItem>();
            using (StreamReader streamReader = new StreamReader(fileName))
            {
                BookItem book;
                while ((book = ReadBookItem(streamReader)) != null)
                {
                    books.Add(book);
                }
            }
            return books;
        }

        static void Main(string[] args)
        {
            string fileName = "../../Data.txt";
            List<BookItem> bookList = ReadBooks(fileName);

            MultiDict<string, BookItem> booksByAutor = new MultiDict<string, BookItem>();
            bookList.ForEach(bk =>
                    bk.Authors.ForEach(autor => booksByAutor.Add(autor, bk))
                );

            string author = "Bond, james";
            Console.WriteLine("Books by: " + author);
            foreach (BookItem book in booksByAutor.GetValues(author))
            {
                Console.WriteLine("    Title : " + book.Title);
            }

            Console.WriteLine("");
            Console.WriteLine("Click to continue");
            Console.ReadKey();
        }
    }
}

而且我还想提一下,如果您用 XML 表示数据,则可以避免所有的解析工作。然后数据看起来像:

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfBookItem >
  <BookItem>
    <Year>1994</Year>
    <Title>For beginners</Title>
    <Book>Accounting</Book>
    <Authors>
      <string>Bond, james</string>
      <string>Smith John A</string>
    </Authors>
    <Editors>
      <string>Smith Joe</string>
      <string>Doe John</string>
    </Editors>
    <Publisher>The University of Chicago Press</Publisher>
    <City>Florida, USA</City>
    <StartPage>15</StartPage>
    <EndPage>23</EndPage>
  </BookItem>
  <BookItem>
    <Year>2000</Year>
    <Title>Medical advances in the modern world</Title>
    <Authors>
      <string>Faux, M</string>
      <string>Sedge, M</string>
      <string>McDreamy, L</string>
      <string>Simbha, D</string>
    </Authors>
    <StartPage>1</StartPage>
    <EndPage>26</EndPage>
    <Issue>2</Issue>
    <Journal>Canadian Journal of medicine</Journal>
    <Volume>25</Volume>
  </BookItem>
  <BookItem>
    <Year>2012</Year>
    <Title>Shape shifting dinosaurs</Title>
    <Authors>
      <string>McFadden, B</string>
      <string>Goodrem, G</string>
    </Authors>
    <City>Vancouver, Canada</City>
    <StartPage>2</StartPage>
    <EndPage>6</EndPage>
    <Conference>Ted Vancouver</Conference>
  </BookItem>
</ArrayOfBookItem>

以及阅读它的代码:

using (FileStream stream =
    new FileStream(@"../../Data.xml", FileMode.Open,
        FileAccess.Read, FileShare.Read))
        {
            List<BookItem> books1 = (List<BookItem>)serializer.Deserialize(stream);
        }
于 2012-05-17T06:16:23.777 回答
2

您可以使用具有以下简单属性的类:

class Book {
    string Title;
    int PageCount;
}

您可以初始化Book[] lines = Book[myFile.LineCount];或维护 a List<Book>,但 string[] 更容易访问单个行号(lines[34]表示第 34 本书和第 34 行)。

但基本上 System.Data.DataTable 可能更适合,因为您的行包含多列。使用 DataTable,您可以访问各个行并按名称访问它们的列。

例子:

DataTable dt = new DataTable();
DataTable.Columns.Add("bookName");

DataRow dr = dt.NewRow();
dr["bookName"] = "The Lost Island";
dt.Rows.Add(dr);

//You can access last row this way: 
dt.Rows[dt.Rows.Count-1]["bookName"].

DataTable 的另一个好处是您可以像在普通 SQL 表上一样对其行使用分组和求和。

编辑:最初我的回答使用了结构,但正如@AndrasZoltan 指出的那样,当您不确定应用程序将演变成什么时,使用类可能会更好。

于 2012-05-15T05:23:32.563 回答
2

你应该创建一个类Book

public class Book
 {
    public string Name { get; set; }
    public string Author { get; set; }
    public string Journal { get; set; }

 }

并保持一个List<Book>

var books = new List<Book>();
books.Add(new Book { Name = "BookName", Author = "Some Auther", Journal = "Journal" });
于 2012-05-15T05:26:06.240 回答
1

您可以为每个项目创建一个类:

class BookItem
        {
            public string Name { get; set; }
            public string Author { get; set; }
        }

将每一行的数据读入此类的一个实例,并将它们存储在一个临时列表中:

var books = new List<BookItem>();
while (NotEndOfFile())
{
    BookItem book= ReadBookItem(...)
    books.Add(book);
}

拥有此列表后,您可以创建多值字典并通过任何键快速访问任何项目。例如,按作者查找一本书:

var booksByAuthor = new MultiDict<string, BookItem>();

将项目添加到字典:

books.ForEach(bk => booksByAuthor.Add(bk.Author, bk));

然后你可以迭代它:

string autorName = "autor1";
Console.WriteLine("Books by: " + autorName);
            foreach (BookItem bk1 in booksByAutor)
            {
                Console.WriteLine("Book: " + bk1.Name);
            }

我从这里得到了基本的多项目词典:

多值字典?

这是我的实现:

class MultiDict<TKey, TValue>  // no (collection) base class
        {
            private Dictionary<TKey, List<TValue>> _data = new Dictionary<TKey, List<TValue>>();

            public void Add(TKey k, TValue v)
            {
                // can be a optimized a little with TryGetValue, this is for clarity
                if (_data.ContainsKey(k))
                    _data[k].Add(v);
                else
                    _data.Add(k, new List<TValue>() { v });
            }

            // more members

            public List<TValue> GetValues(TKey key)
            {
                if (_data.ContainsKey(key))
                    return _data[key];
                else
                    return new List<TValue>();
            }

        }
于 2012-05-15T06:13:51.070 回答
1

如果没有更好的文件示例或您希望如何使用数据,您还不清楚您需要什么,但听起来您需要解析字符串并将其放入实体中。以下是使用您上面提到的字段的示例。

public IList<Entry> ParseEntryFile(string fileName)
{
    ...
    var entries = new List<Entry>();

    foreach(var line in file)
    {
        var entry = new Entry();
        ...
        entries.Add(entry);
    }
    return entries;
}


public class Entry
{
    public Book BookEntry { get; set; }
    public Author AuthorEntry { get; set; }
    public Journal JournalEntry { get; set; }
}

public class Book
{
    public string Name{ get; set; }
    ...
}

public class Author
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

...
于 2012-05-15T05:37:50.477 回答