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.
- Seek4: Time elapsed: 00:00:00.0398530 ms -- Ritch Melton
- Seek3: Time elapsed: 00:00:00.0446072 ms -- Valentin Kuzub
- Seek1: Time elapsed: 00:00:00.0538210 ms -- Jake
- 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.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich bin verwirrt von Ihren erwarteten Positionen, Linie 5 an position 30 und 45, mit der Linie 4 in 15, und 3 bei 30?
Hier ist der Kern der Logik Lesen:
Die vollständige Probe gibt es hier:
Legen Sie diese in die innere Schleife des
SeekMethod(int[] lineNos)
:Das problem ist, dass Ihr
position
variable Veränderungen basierend auf seinen vorherigen Wert, undStreamReader
verwaltet einen Puffer auf, so dass Sie brauchen, um die gepufferten Daten, wenn Sie ändern Sie die stream-position.reader.DiscardBufferedData();
hilft. Ich habe es vor dem Ende der Schleife. Ich werde die anderen Antworten auch nur zu prüfen, wenn es gibt mir schnellere Ergebnisse. DankDu hast ziemlich kranke Mischung aus position absolute für die ersten lineno und Angehörigen, für weitere lineno ist
Genau hinsehen und die tatsächlichen Ergebnisse Ihr immer
Werte 3,4,5 erhalten Sie zahlen 30, 15, 45, während seine offensichtlich, dass, wenn Ihr mit der relativen position, die es sein sollte 30,15,15 da die Länge der Linie beträgt 15 ODER 30,0,0, wenn der read-Methode, führt die SUCHT als Nebenwirkung, wie filestream.Lesen tut. Und Ihre test-Ausgabe ist VERSEHENTLICH das richtige (nur für string-Werte jedoch nicht Positionen) sind , sollten Sie verwendet haben, nicht eine Folge für test und schauen Wert für die position genauer zu sehen, dass es keine Verbindung mit der Zeichenfolge angezeigt, und der Wert für die position.
Eigentlich Ihre StreamReader ist das ignorieren weitere
fs.Seek
Gespräche und ist einfach zu Lesen Zeile =)Hier ist Ergebnisse für 3 5 9 input 🙂
Ich glaube, Folgendes ist dem am nächsten, was Ihr zu erreichen versucht, eine neue Funktion
Und die neue loop-code
Beachten Sie, dass jetzt
stream.Read
Funktion ist äquivalent zu zusätzlichenstream.Seek (Length)
Neuen richtige Ausgang und logische position ändert
PS: es ist so seltsam, Sie denken, dass 001: Zeile 1. Zeile nicht 0. .. das ganze
-1
könnte entfernt werden, wenn Sie verwendet Programmierer count-Methode =)Ich würde nicht sagen, das problem ist Ihr Aufwand für die manuelle Verwaltung von der position Wert, sondern eher, dass StreamReader.ReadLine änderungen der stream-Position Wert. Wenn Sie Schritt für Schritt durch den code und überwachen Sie Ihre lokalen Werte, sehen Sie die stream-position ändert sich nach jedem ReadLine-Aufruf (bis 148 nach der ersten).
BEARBEITEN
Wäre es besser, einfach nur die stream-position direkt, anstatt zu Suchen
Das problem ist, du bist die Verfolgung der position manuell, aber nicht die Rechnungslegung für die Tatsache, dass der eigentliche Datei-position wird eine Zeile weiter in nach die Sie habe gelesen, dass Linie. So müssen Sie subtrahieren sich heraus, dass zusätzliche Lesen --- aber nur, wenn es wirklich passiert ist.
Wenn Sie wirklich wollen, es zu tun auf diese Weise, dann anstatt zu halten
position
um, Holen Sie sich die aktuelle Dateiposition; oder die Berechnung der "absolute file" position, aus der gegebenen Zeilennummer ad suchen, sondern direkt aus dem aktuellen Datei-offset.// Dynamic positioning position = (lineNo - 1) * LineLength - position; fs.Seek(position, SeekOrigin.Current);
zu// Absolute positioning position = (lineNo - 1) * LineLength; fs.Seek(position, SeekOrigin.Begin);
ich bekomme immer noch die gleiche falsche Ergebnis.