3

文字列のブロックのファイルがあり、それぞれが特定のキーワードで終わります。現在、現在のブロックの終わりまでファイルの各行をリストに追加するストリームリーダーのセットアップがあります(行にはブロックの終わりを示すキーワードが含まれています)。

listName.Add(lineFromFile);

各ブロックには、Book bookName、Author AuthorName、Journal JournalNameなどの情報が含まれています。したがって、各ブロックは仮想的に単一のアイテム(本、ジャーナル、会議など)です。

今では約50ブロックの情報(アイテム)があり、情報を操作して各作成者、タイトル、ページなどを保存し、どの情報がどのアイテムに対応するかを知ることができるように、情報を保存する方法が必要です。

これを入力しているときに、各Itemを「Item」というクラスのオブジェクトとして格納する可能性があるという考えを思いつきましたが、複数の作成者がいる可能性があるため、これを実現する方法がわかりません。変数に名前を付けるためのカウンター。

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]

2つのソリューションを紹介します。1つはを使用しDictionary、もう1つはを使用し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は、引用情報の改行を含む1つのブロックである必要があることに注意してください。

次に、作成者ごとに各情報を保存する辞書を作成します。

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

OrdinalIgnoreCase比較者に注意してください-著者の名前が別の大文字小文字で印刷される状況を処理します。

List<string>あなたがあなたのように追加しているものを考えると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のリストになります。との2つのアイテムが同じリストにグループ化されるように大文字と小文字を区別せずにグループ化している場合でも、の大文字と小文字はファイルから保持されますが、元の大文字と小文字はで保持されることInfoに注意してください。AuthorName"jon skeet""Jon Skeet"Info

また、コードはInfo、引用ごとに1つのインスタンスのみが作成されるように記述されています。これは、多くの理由(メモリ、集中更新など)で推奨されます。

または、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オブジェクトはその作成者名を持つすべてのオブジェクトです。ここでも、「2つのスキート」に関する同じケース保存動作が観察されます。

于 2012-05-15T05:32:28.997 に答える
2

次のような単純な属性を持つクラスを使用できます。

class Book {
    string Title;
    int PageCount;
}

を初期化Book[] lines = Book[myFile.LineCount];または維持することができます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のもう1つの優れた点は、通常の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 に答える
2

これには複数値の辞書を使用します。

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

次に、辞書オブジェクトを作成します。

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

このように、複数の作成者に遭遇した場合、データは作成者ごとに並べ替えられるため、このデータを処理するための将来のコードを簡単に作成できます。一部の著者の下ですべての本のリストを印刷することは非常に簡単で、面倒な検索プロセスを必要としません。

于 2012-05-15T05:35:56.560 に答える
2

リレーショナルデータベースの発明に向けて順調に進んでいます。便利なことに、これらはすでに利用可能です。エンティティ間の関係を格納する問題を解決することに加えて、それらは並行性の問題も処理し、証明可能な数学に基づいたモデリング手法によってサポートされます。


パーサーはそれ自体が対象です。SQLは問題外であり、これは不自然な大学の課題であるため、私はいくつかの見解を持っています。

  • 簡単な方法は正規表現を使用することです。ただし、これは非常に非効率的であり、大きな入力ファイルには不十分なソリューションです。
  • 正規表現がない場合は、String.IndexOf()とString.Split()が友達です。
  • 評価者がSQLに対応できない場合、LINQは非常にショックを受けるでしょうが、私はZoltanのLINQソリューションが本当に好きで、とてもエレガントです。
于 2012-05-15T05:49:25.200 に答える
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 に答える
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);
}

このリストを取得したら、Multi Value Dictionariesを作成し、任意のキーで任意のアイテムにすばやくアクセスできます。たとえば、著者による本を見つけるには、次のようにします。

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 に答える