简体   繁体   English

从 C# 中的文本文件一次读取 10 行

[英]Read 10 lines at a time from text file in C#

I'm looking for a solution to read 10 lines from a text files and then more 10 lines until the end of the file.我正在寻找一种解决方案,从文本文件中读取 10 行,然后再读取 10 行,直到文件末尾。 This is what I started, of course it shows me the first 10 lines, but how can I repeat this process for the next 10 lines and so on, until the end of the file?这是我开始的,当然它显示了前 10 行,但是我怎样才能在接下来的 10 行中重复这个过程等等,直到文件结束?

private void openFile_Click(object sender, EventArgs e)
{
    int counter = 0;
    string line;

    using (var file =
       new System.IO.StreamReader(@"C:\\Users\\LJ_TEX\\Desktop\\Book1.txt"))
    {
        while ((line = file.ReadLine()) != null)
        {
            counter++;
            if (counter <= 10)
            {
                tboxreadData.AppendText(line + '\r' + '\n');
            }
            if (counter == 10)
            {
                tboxreadData.AppendText("NEXT");
            }
        }
    }
}

EDIT编辑

So I managed to make a little change, show one line at a click of the button with this所以我设法做了一点改变,点击按钮显示一行

 System.IO.StreamReader file = null;

 private void openFile_Click(object sender, EventArgs e)
    {
        string line;

        if (file == null)
            file = new System.IO.StreamReader(@"C:\\Users\\LJ_TEX\\Desktop\\Book1.txt");

        if (!file.EndOfStream)
        {
            line = file.ReadLine();
            tboxreadData.AppendText(line + '\r' + '\n');
        }
        else
        {
            tboxreadData.AppendText("End");
        }
       
    }

Sure, it only shows one line line at a time.当然,它一次只显示一条线。 If anyone have a idea how to show more lines, 5 or 10, feel free to share.如果有人知道如何显示更多行(5 或 10 行),请随时分享。

Thanks, Jonathan谢谢,乔纳森

You can achieve that by using the modulo operator (%).您可以通过使用模运算符 (%) 来实现。 So you check if your counter%10=0 to print out the "NEXT"-Line.所以你检查你的counter%10=0是否打印出“NEXT”-Line。

Your example could look like this:您的示例可能如下所示:

private void openFile_Click(object sender, EventArgs e)
    {
        int counter = 0;
        string line;

        System.IO.StreamReader file = new System.IO.StreamReader(@"C:\\Users\\LJ_TEX\\Desktop\\Book1.txt");

        while ((line = file.ReadLine()) != null)
        {
            counter++;               
            tboxreadData.AppendText(line + '\r' + '\n');
            if(counter%10 == 0){
                tboxreadData.AppendText("NEXT");
            }
        }
    }

EDIT:编辑:

For read everytime 10 lines (if present), you could use the following snippet.对于每次读取 10 行(如果存在),您可以使用以下代码段。 It isnt performant, because you will read everytime from the beginning:它不是高性能的,因为您每次都会从头开始阅读:

int _lineCounter = 0;

private void openFile_Click(object sender, EventArgs e)
    {
        int counter = 0;
        string line;

        System.IO.StreamReader file = new System.IO.StreamReader(@"C:\\Users\\LJ_TEX\\Desktop\\Book1.txt");

        while ((line = file.ReadLine()) != null)
        {
            counter++;
            if(counter < _linecounter)
                continue;
            tboxreadData.AppendText(line + '\r' + '\n');
            if(counter%10 == 0){
                tboxreadData.AppendText("NEXT");
                break;
            }
        }
        _lineCounter = counter;
    }

You could create your own extension method which takes an IEnumerbale<T> and partitions it in the chunks.您可以创建自己的扩展方法,该方法采用IEnumerbale<T>并将其划分为块。 From there you can iterate it with a foreach or any standard linq style chain method从那里您可以使用foreach或任何标准 linq 样式链方法对其进行迭代

Given给定

// updated, this is completely lazy loaded
public static class Extensions
{
   public static IEnumerable<IEnumerable<TValue>> Partition<TValue>(this IEnumerable<TValue> source, int size)
   {
      using var enumerator = source.GetEnumerator();
      static IEnumerable<T> GetChunk<T>(IEnumerator<T> enumerator, int chunkSize)
      {
         for (var i = 0; i < chunkSize && enumerator.MoveNext(); i++)
            yield return enumerator.Current;
      }

      while (enumerator.MoveNext())
         yield return GetChunk(enumerator, size);
   }
}

Usage用法

var partitions = File
     .ReadLines(@"D:\ids.txt")
     .Partition(10);

foreach (var partition in partitions)
{
   Console.WriteLine("Start partition");
   foreach (var line in partition )
      Console.WriteLine(line);
}

Output Output

Start partition
1516556 E763794 1773595 33EBM5015703
1516556 E763794 1773657 33EBM5015703
1518933 E764710 1776347 33EBM5015705
1519147 H182989 1776617 33EBM5015702
1519264 H183025 1776745 33EBM5015686
1519310 X186946 1776805 33EBM5015695
1519622 X186992 1777159 33EBM5015704
1519623 E765015 1777160 33EBM5015716
1519818 H183205 1777375 33EBM5015681
1519932 E765141 1777546 33EBM5015700
Start partition
1520345 E765295 1778070 33EBM5015715
1520366 E765303 1778097 33EBM5015684
1520385 X187075 1778117 33EBM5015675
1520420 H183413 1778157 33EBM5015662
1520429 H183418 1778166 33EBM5015654
1520466 X187085 1778203 33EBM5015663
1520468 E765345 1778205 33EBM5015658
1520476 E765349 1778214 33EBM5015677
1520486 H183441 1778224 33EBM5015664
1520496 H183444 1778234 33EBM5015671
Start partition
1520506 E765361 1778244 33EBM5015666
1520510 E765364 1778248 33EBM5015670
1520528 H183462 1778270 33EBM5015680
1520550 H183474 1778292 33EBM5015653
1520553 X187092 1778295 33EBM5015706
1520558 E765382 1778300 33EBM5015650
1520574 E765389 1778316 33EBM5015656
1520585 E765396 1778327 33EBM5015669
1520618 X187102 1778360 33EBM5015682
1520621 E765408 1778363 33EBM5015667

What you want is quite common: you want to read your input "per page".你想要的很常见:你想“每页”阅读你的输入。

In other words: you have a sequence of similar items, and you want to divide this into subgroups of equal size.换句话说:你有一系列相似的项目,你想把它分成大小相等的子组。

If you will be using this regularly, consider to create some classes for it.如果您将定期使用它,请考虑为其创建一些类。 This way you can use it for several problems where you need to fetch items "per page".这样,您可以将它用于需要“每页”获取项目的几个问题。

I regularly use it to fetch items from a database per page.我经常使用它从每页的数据库中获取项目。 Because of the generic classes, I can put an IQueryable / IEnumerable in a PageCollection class and ask for the number of pages, and for Page[3].由于通用类,我可以将 IQueryable / IEnumerable 放入 PageCollection class 并询问页数和 Page[3]。

If you do it smart, you won't have to fetch items that you don't use, and you don't re-fetch items that you already fetched.如果你做的很聪明,你就不必获取你不使用的项目,也不需要重新获取你已经获取的项目。

We hide the internals of a page.我们隐藏页面的内部。 Therefore we create an interface:因此我们创建了一个接口:

interface IPage<T> : IReadOnlyCollection<T>, IEnumerable<T>
{
    int PageNr {get; }             // the zero based number of the page
    int PageCount {get; }          // the total number of available pages
    int PageLength {get; }         // The number of items on this page

I chose to implement IReadOnlyCollection<T> instead of IReadOnlyList<T> , because the index usually gives the incorrect impression.我选择实现IReadOnlyCollection<T>而不是IReadOnlyList<T> ,因为索引通常给人的印象不正确。

For example, if you have a collection of ProductPages , then every ProductPage has zero or more Products .例如,如果您有一个ProductPages集合,那么每个ProductPage都有零个或多个Products But what do you expect if you are on ProductPage[10] , and ask for Product[3] ?但是,如果您在ProductPage[10]上并要求Product[3] ,您会期待什么? Some might confuse this with the Product with primary key 3.有些人可能会将其与具有主键 3 的Product混淆。

Maybe the following methods might also be handy:也许以下方法也可能很方便:

    IPage<T> PreviousPage {get;}
    IPage<T> NextPage {get;}
    IPage<T> GetPage(int pageNr);
    IReadOnlyList<T> PageCollection {get;}
}

First let's create the PageCollection.首先让我们创建 PageCollection。 The PageCollection will create the pages: PageCollection 将创建页面:

class PageCollection<T> : IReadOnlyList<T>
{
    private readonly IDictionary<int, IPage<T>> fetchedPages
        = new Dictionary<int, IPage<T>();

    private int pageCount = -1; // not known yet

    public PageCollection<IEnumerable<T> query, pageSize)
    {
        // TODO: check input parameters for incorrect values
        this.Query = query;
        this.PageSize = pageSize;
    }

    public IEnumerable<T> Query {get;} 
    // TODO: consider IQueryable if you use databases / entity framework

    public int PageSize {get;}
    ...
}

We need methods to fetch the number of Pages, and to fetch a page by index:我们需要一些方法来获取页面的数量,并通过索引来获取页面:

public int Count
{
    get
    {
        if (this.pageCount < 0)
           this.pageCount = this.Query.Count();
        return this.pageCount;
    }
}

public IPage this[int pageIndex] => this.GetPage(pageIndex);

Finally we come to the part where a page is created:最后我们来到创建页面的部分:

public IPage<T> GetPage(int pageIndex)
{
    if (0 < pageIndex || pageIndex >= this.Count)
    {
        // pageIndex out of range.
        // TODO: decide whether to return null or throw exception
    }

    if (!this.fetchedPages.TryGetValue(pageIndex, out Page<T> fetchedPage)
    {
        // page never fetched before, fetch it now
        fetchedPage = this.FetchPage(pageIndex);
        this.FetchedPages.Add(pageIndex, fetchedPage);
    }
    return fetchedPage;
}

I decided to save fetched pages in a Dictonary, rather than in a List.我决定将获取的页面保存在字典中,而不是列表中。 This way you can ask for Page[5] before you have fetched pages 0 to 4.这样,您可以在获取页面 0 到 4 之前请求 Page[5]。

private Page<T> FetchPage(int pageIndex)
{
    return new Page(this, pageIndex);
}

Well, that didn't do much: apparently it is the page that does all the work.好吧,这并没有多大作用:显然它是完成所有工作的页面。 Time to create the Page.是时候创建页面了。

You'll have to decide for yourself whether you read the complete page immediately, or only when you ask for it您必须自己决定是立即阅读完整页面,还是仅在您要求时阅读

class Page<T> : IPage<T>, IReadOnlyCollection<T>, IEnumerable<T>
{
    public Page(PageCollection<T> pageCollection, int pageNr)
    {
         this.PageCollection = pageCollection;
         this.PageNr = pageNr;

         // if you want to fetch the data immediately:
         this.PageContent = this.Query.ToList();
    }

    public PageCollection<T> PageCollection {get;}
    public int PageNr {get;}
    public int PageCount => this.PageCollection.Count;
    public IReadOnlyCollection<T> PageContent {get;}

    public IEnumerable<T> Query => this.PageCollection.Query
        .Skip(this.PageNr * this.PageSize)
        .Take(this.PageSize);
}

The implementations of IReadOnlyCollection<T> and IEnumerable<T> are fairly straight forward, the methods all call this.PageContent : IReadOnlyCollection<T>IEnumerable<T>的实现相当简单,方法都调用this.PageContent

IEnumerator<T> GetEnumerator() {return this.PageContent.GetEnumerator();}
int Count => this.PageContent.Count;

etc.等等

"Nice to have" procedures like PreviousPage / NextPage / GetPage are one-liners, because they can be handled by asking the PageCollection:像 PreviousPage / NextPage / GetPage 这样的“很高兴”过程是单行的,因为它们可以通过询问 PageCollection 来处理:

IPage<T> PreviousPage => this.PageCollection.GetPage(this.PageNr-1);

Of course you have to decide what to do if Page gets out of range: exception or return null?当然,您必须决定如果 Page 超出范围该怎么办:异常还是返回 null?

Finally usage:最后使用:

const int pageSize = 25;
IEnumerable<Product> queryProducts = ...
PageCollection<Product> productPages =
   new PageCollection<Product>(queryProducts, pageSize);

Page<Product> productPage = productPages.FirstOrDefault();
// this page can be used as a collection of Products
DisplayProducts(productPage);

// get the next page:
Page<Product> nextPage = productPage.NextPage;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM