Die Verwendung Von FileStream.Suchen

Ich versuche zu arbeiten mit FileStream.Suchen, um schnell den Sprung zu einer Zeile, und Lesen Sie es.

Allerdings bin ich nicht immer die richtigen Ergebnisse. Ich habe versucht, sich auf diese für eine Weile und kann nicht verstehen, was ich falsch mache.

Umwelt:

OS: Windows 7

Framework: .NET 4.0

IDE: Visual C# Express 2010

Sample-Daten in der Datei-Speicherort: C:\Temp\Temp.txt

0001/100!2500 
0002/100!2500 
0003/100!2500 
0004/100!2500 
0005/100!2500 
0006/100!2500 
0007/100!2500 
0008/100!2500 
0009/100!2500 
0010/100!2500 

Code:

class PaddedFileSearch
{
    private int LineLength { get; set; }
    private string FileName { get; set; }

    public PaddedFileSearch()
    {
        FileName = @"C:\Temp\Temp.txt";     //This is a padded file.  All lines are of the same length.

        FindLineLength();
        Debug.Print("File Line length: {0}", LineLength);

        //TODO: This purely for testing.  Move this code out.
        SeekMethod(new int[] { 5, 3, 4 });
        /*  Expected Results:
         *  Line No     Position        Line
         *  -------     --------        -----------------
         *  3           30              0003|100!2500
         *  4           15              0004|100!2500
         *  5           15              0005|100!2500 -- This was updated after the initial request.
         */

        /* THIS DOES NOT GIVE THE EXPECTED RESULTS */
        SeekMethod(new int[] { 5, 3 });
        /*  Expected Results:
         *  Line No     Position        Line
         *  -------     --------        -----------------
         *  3           30              0003|100!2500
         *  5           30              0005|100!2500
         */
    }

    private void FindLineLength()
    {
        string line;

        //Add check for FileExists

        using (StreamReader reader = new StreamReader(FileName))
        {
            if ((line = reader.ReadLine()) != null)
            {
                LineLength = line.Length + 2;
                //The 2 is for NewLine(\r\n)
            }
        }

    }

    public void SeekMethod(int[] lineNos)
    {
        long position = 0;
        string line = null;

        Array.Sort(lineNos);

        Debug.Print("");
        Debug.Print("Line No\t\tPosition\t\tLine");
        Debug.Print("-------\t\t--------\t\t-----------------");

        using (FileStream fs = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (StreamReader reader = new StreamReader(fs))
            {
                foreach (int lineNo in lineNos)
                {
                    position = (lineNo - 1) * LineLength - position;
                    fs.Seek(position, SeekOrigin.Current);

                    if ((line = reader.ReadLine()) != null)
                    {
                        Debug.Print("{0}\t\t\t{1}\t\t\t\t{2}", lineNo, position, line);
                    }
                }
            }
        }
    }
}

Die Ausgabe bekomme ich:

Datei-Linie Länge: 15 

Zeile Nicht In Der Position Line 
------- -------- ----------------- 
3 30 0003/100!2500 
4 15 0004/100!2500 
5 45 0005/100!2500 

Zeile Nicht In Der Position Line 
------- -------- ----------------- 
3 30 0003/100!2500 
5 30 0004/100!2500 

Mein problem ist mit der folgenden Ausgabe:

Linie Nicht In Der Position Line 
------- -------- ----------------- 
5 30 0004/100!2500 

Den Ausgang für die Linie sein sollte: 0005/100!2500

Ich verstehe nicht, warum dies geschieht.

Mache ich etwas falsch?
Gibt es eine Abhilfe?
Auch gibt es schnellere Wege, dies zu tun mit so etwas wie suchen?

(Ich bin auf der Suche nach code-basierte Optionen und NICHT Oracle oder SQL Server. For the sake of argument können Sie auch sagen, dass die Dateigröße 1 GB.)

Jede Hilfe wird sehr geschätzt.

Dank.

UPDATE:

Ich fand 4 tolle Antworten hier. Vielen Dank.

Sample-Timings:

Basierend auf ein paar runs im folgenden werden die Methoden aus best zu gute. Sogar das gute liegt sehr nah zum besten.

In einer Datei mit 10K Zeilen, 2.28 MB. Ich suchte gleich 5000 zufällige Linien mit allen Optionen.

  1. Seek4: Time elapsed: 00:00:00.0398530 ms -- Ritch Melton
  2. Seek3: Time elapsed: 00:00:00.0446072 ms -- Valentin Kuzub
  3. Seek1: Time elapsed: 00:00:00.0538210 ms -- Jake
  4. Seek2: Time elapsed: 00:00:00.0889589 ms -- bitxwise

Unten ist der code. Nach dem speichern der code, den Sie einfach aufrufen kann es durch Eingabe TestPaddedFileSeek.CallPaddedFileSeek();. Sie haben auch den namespace angeben und "mit Referenzen".

`

///<summary>
///This class multiple options of reading a by line number in a padded file (all lines are the same length).
///The idea is to quick jump to the file.
///Details about the discussions is available at: http://stackoverflow.com/questions/5201414/having-a-problem-while-using-filestream-seek-in-c-solved
///</summary>
class PaddedFileSeek
{
    public FileInfo File {get; private set;}
    public int LineLength { get; private set; }

    #region Private methods
    private static int FindLineLength(FileInfo fileInfo)
    {
        using (StreamReader reader = new StreamReader(fileInfo.FullName))
        {
            string line;
            if ((line = reader.ReadLine()) != null)
            {
                int length = line.Length + 2;   //The 2 is for NewLine(\r\n)
                return length;
            }
        }
        return 0;
    }

    private static void PrintHeader()
    {
       /*
        Debug.Print("");
        Debug.Print("Line No\t\tLine");
        Debug.Print("-------\t\t--------------------------");
       */ 
    }

    private static void PrintLine(int lineNo, string line)
    {
        //Debug.Print("{0}\t\t\t{1}", lineNo, line);
    }

    private static void PrintElapsedTime(TimeSpan elapsed)
    {
        Debug.WriteLine("Time elapsed: {0} ms", elapsed);
    }
    #endregion

    public PaddedFileSeek(FileInfo fileInfo)
    {
        //Possibly might have to check for FileExists
        int length = FindLineLength(fileInfo);
        //if (length == 0) throw new PaddedProgramException();
        LineLength = length;
        File = fileInfo;
    }

    public void CallAll(int[] lineNoArray, List<int> lineNoList)
    {
        Stopwatch sw = new Stopwatch();

        #region Seek1
        //Create new stopwatch
        sw.Start();

        Debug.Write("Seek1: ");
        //Print Header
        PrintHeader();

        Seek1(lineNoArray);

        //Stop timing
        sw.Stop();

        //Print Elapsed Time
        PrintElapsedTime(sw.Elapsed);

        sw.Reset();
        #endregion

        #region Seek2
        //Create new stopwatch
        sw.Start();

        Debug.Write("Seek2: ");
        //Print Header
        PrintHeader();

        Seek2(lineNoArray);

        //Stop timing
        sw.Stop();

        //Print Elapsed Time
        PrintElapsedTime(sw.Elapsed);

        sw.Reset();
        #endregion

        #region Seek3
        //Create new stopwatch
        sw.Start();

        Debug.Write("Seek3: ");
        //Print Header
        PrintHeader();

        Seek3(lineNoArray);

        //Stop timing
        sw.Stop();

        //Print Elapsed Time
        PrintElapsedTime(sw.Elapsed);

        sw.Reset();
        #endregion

        #region Seek4
        //Create new stopwatch
        sw.Start();

        Debug.Write("Seek4: ");

        //Print Header
        PrintHeader();

        Seek4(lineNoList);

        //Stop timing
        sw.Stop();

        //Print Elapsed Time
        PrintElapsedTime(sw.Elapsed);

        sw.Reset();
        #endregion

    }

    ///<summary>
    ///Option by Jake
    ///</summary>
    ///<param name="lineNoArray"></param>
    public void Seek1(int[] lineNoArray)
    {
        long position = 0;
        string line = null;

        Array.Sort(lineNoArray);

        using (FileStream fs = new FileStream(File.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (StreamReader reader = new StreamReader(fs))
            {
                foreach (int lineNo in lineNoArray)
                {
                    position = (lineNo - 1) * LineLength;
                    fs.Seek(position, SeekOrigin.Begin);

                    if ((line = reader.ReadLine()) != null)
                    {
                        PrintLine(lineNo, line);
                    }

                    reader.DiscardBufferedData();
                }
            }
        }

    }

    ///<summary>
    ///option by bitxwise
    ///</summary>
    public void Seek2(int[] lineNoArray)
    {
        string line = null;
        long step = 0;

        Array.Sort(lineNoArray);

        using (FileStream fs = new FileStream(File.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            //using (StreamReader reader = new StreamReader(fs))
            //If you put "using" here you will get WRONG results.
            //I would like to understand why this is.
            {
                foreach (int lineNo in lineNoArray)
                {
                    StreamReader reader = new StreamReader(fs);
                    step = (lineNo - 1) * LineLength - fs.Position;
                    fs.Position += step;

                    if ((line = reader.ReadLine()) != null)
                    {
                        PrintLine(lineNo, line);
                    }
                }
            }
        }
    }

    ///<summary>
    ///Option by Valentin Kuzub
    ///</summary>
    ///<param name="lineNoArray"></param>
    #region Seek3
    public void Seek3(int[] lineNoArray)
    {
        long position = 0; //totalPosition = 0;
        string line = null;
        int oldLineNo = 0;

        Array.Sort(lineNoArray);

        using (FileStream fs = new FileStream(File.FullName, FileMode.Open, FileAccess.Read, FileShare.None))
        {
            using (StreamReader reader = new StreamReader(fs))
            {
                foreach (int lineNo in lineNoArray)
                {
                    position = (lineNo - oldLineNo - 1) * LineLength;
                    fs.Seek(position, SeekOrigin.Current);
                    line = ReadLine(fs, LineLength);
                    PrintLine(lineNo, line);
                    oldLineNo = lineNo;

                }
            }
        }

    }

    #region Required Private methods
    ///<summary>
    ///Currently only used by Seek3
    ///</summary>
    ///<param name="stream"></param>
    ///<param name="length"></param>
    ///<returns></returns>
    private static string ReadLine(FileStream stream, int length)
    {
        byte[] bytes = new byte[length];
        stream.Read(bytes, 0, length);
        return new string(Encoding.UTF8.GetChars(bytes));
    }
    #endregion
    #endregion

    ///<summary>
    ///Option by Ritch Melton
    ///</summary>
    ///<param name="lineNoArray"></param>
    #region Seek4
    public void Seek4(List<int> lineNoList)
    {
        lineNoList.Sort();

        using (var fs = new FileStream(File.FullName, FileMode.Open))
        {
            lineNoList.ForEach(ln => OutputData(fs, ln));
        }

    }

    #region Required Private methods
    private void OutputData(FileStream fs, int lineNumber)
    {
        var offset = (lineNumber - 1) * LineLength;

        fs.Seek(offset, SeekOrigin.Begin);

        var data = new byte[LineLength];
        fs.Read(data, 0, LineLength);

        var text = DecodeData(data);
        PrintLine(lineNumber, text);
    }

    private static string DecodeData(byte[] data)
    {
        var encoding = new UTF8Encoding();
        return encoding.GetString(data);
    }

    #endregion

    #endregion
}



static class TestPaddedFileSeek
{
    public static void CallPaddedFileSeek()
    {
        const int arrayLenght = 5000;
        int[] lineNoArray = new int[arrayLenght];
        List<int> lineNoList = new List<int>();
        Random random = new Random();
        int lineNo;
        string fileName;


        fileName = @"C:\Temp\Temp.txt";

        PaddedFileSeek seeker = new PaddedFileSeek(new FileInfo(fileName));

        for (int n = 0; n < 25; n++)
        {
            Debug.Print("Loop no: {0}", n + 1);

            for (int i = 0; i < arrayLenght; i++)
            {
                lineNo = random.Next(1, arrayLenght);

                lineNoArray[i] = lineNo;
                lineNoList.Add(lineNo);
            }

            seeker.CallAll(lineNoArray, lineNoList);

            lineNoList.Clear();

            Debug.Print("");
        }
    }
}

`

  • Sind Sie fehlenden code? Ich sehe nicht deine Datei, reAd/seek Teil
  • Es ist dort, in der SeekMethod Methode.
  • Ich nicht bekommen, Ihre Positionen. Wenn die Linie 1 ist 0, 2 15, 3 30, 4 45(nicht 15?), 5 ist 60 (nicht 30?) richtig?
  • Du hast Recht. Ich war so konzentriert auf der LINIE zur Folge, dass ich gar nicht gemerkt, dass.
InformationsquelleAutor Pranav Shah | 2011-03-05
Schreibe einen Kommentar