À L'Aide De FileStream.Chercher
Je suis en train de travailler avec FileStream.Chercher à aller rapidement à une ligne et de le lire.
Cependant, je ne suis pas d'obtenir les résultats de la droite. J'ai essayé de regarder cela pendant un certain temps et ne peut pas comprendre ce que je fais de mal.
Environnement:
Système d'exploitation: Windows 7
Cadre: .NET 4.0
IDE: Visual C# Express 2010
Échantillon de Données dans le fichier de localisation: 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
Le 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);
}
}
}
}
}
}
La sortie j'obtiens:
Fichier de la longueur de la Ligne: 15 Pas De Position De La Ligne De ------- -------- ----------------- 3 30 0003/100!2500 4 15 0004/100!2500 5 45 0005/100!2500 Pas De Position De La Ligne De ------- -------- ----------------- 3 30 0003/100!2500 5 30 0004/100!2500
Mon problème est avec la sortie suivante:
Ligne Pas De Poste En Ligne ------- -------- ----------------- 5 30 0004/100!2500
La sortie de la Ligne doit être: 0005/100!2500
Je ne comprends pas pourquoi ce qui se passe.
Je fais quelque chose de mal?
Est-il une solution?
Aussi y at-il des façons plus rapides pour ce faire, utilisez quelque chose comme chercher?
(Je suis à la recherche pour le code en fonction des options et des PAS Oracle ou SQL Server. Pour la clarté de l'exposé permet de dire aussi que la taille du fichier de 1 GO.)
Toute aide est grandement appréciée.
Grâce.
Mise à JOUR:
J'ai trouvé 4 grandes réponses ici. Merci beaucoup.
Échantillon Horaires:
Basé sur les quelques pistes suivantes sont les méthodes de meilleur pour de bon. Même le bon est très proche des meilleurs.
Dans un fichier qui contient 10K lignes, 2.28 MB. J'ai cherché pour le même 5000 lignes au hasard à l'aide de toutes les options.
- Seek4: Temps écoulé: 00:00:00.0398530 ms -- Ritch Melton
- Seek3: Temps écoulé: 00:00:00.0446072 ms -- Valentin Kuzub
- Seek1: Temps écoulé: 00:00:00.0538210 ms -- Jake
- Seek2: Temps écoulé: 00:00:00.0889589 ms -- bitxwise
Indiqué ci-dessous est le code. Après l'enregistrement du code, vous pouvez simplement appeler en tapant TestPaddedFileSeek.CallPaddedFileSeek();
. Vous devrez également spécifier l'espace de noms et le "à l'aide de références".
`
///<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("");
}
}
}
`
- Êtes-vous code manquant? Je ne vois pas votre fichier en lecture/recherche partie
- C'est là, dans la SeekMethod méthode.
- Je ne comprends pas votre position. Si la ligne 1 est 0, 2, 15, 3, 30, 4 est de 45(15?), 5 est de 60 (au lieu de 30?) - il correct?
- Vous êtes de droite. J'étais tellement concentrée sur la LIGNE de résultat, je n'ai même pas remarqué.
Vous devez vous connecter pour publier un commentaire.
Je suis confus par vos positions, sur la Ligne 5 à la position 30 et 45 ans, avec la Ligne 4 à 15, et de 3 à 30?
Voici la base de la lecture de la logique:
L'échantillon complet est ici:
De le placer dans la boucle intérieure de la
SeekMethod(int[] lineNos)
:Le problème, c'est que votre
position
la modification de la variable basée sur sa valeur précédente, etStreamReader
gère une mémoire tampon de sorte que vous devez effacer les données en mémoire tampon lorsque vous modifiez le flux de position.reader.DiscardBufferedData();
ne l'aide. Je l'ai ajouté avant la fin de la boucle. Je vais regarder les autres réponses, aussi bien tout simplement pour vérifier si il me donne des résultats plus rapidement. MerciVous avez assez malade mélange de position absolue pour la première lineno et relative pour de plus amples lineno de
Regardez de plus près ici, et les résultats réels de votre prise en
Pour les valeurs de 3,4,5-vous obtenir les numéros 30, 15, 45, alors que son évident que si votre utilisation de la position relative, il convient de 30,15,15 depuis la longueur de la ligne est de 15 OU 30,0,0 si votre méthode de lecture effectue CHERCHER comme effet secondaire, comme filestream.Lire ne. Et votre test de sortie est ACCIDENTELLEMENT correct (uniquement pour les valeurs de chaîne si, pas de positions) , vous devriez avoir utilisé pas une séquence de test et de regarder la valeur de position de plus près pour voir qu'il n'y a pas de connexion avec la chaîne affichée et la valeur de position.
Fait votre StreamReader est ignorant encore
fs.Seek
appels et qui est tout simplement la lecture ligne par ligne,=)Voici les résultats pour les 3 5 9 entrée 🙂
Je crois suivantes est la plus proche de ce que vous essayez d'atteindre, une nouvelle fonction
Et le nouveau code de boucle
Remarquer que maintenant
stream.Read
fonction est équivalente à d'autresstream.Seek (Length)
Nouveau corriger de sortie et de la logique des changements de position
PS: c'est tellement bizarre que vous pensez que 001: ligne 1ère ligne pas 0e .. que tout
-1
pourrait être supprimée si vous avez utilisé programmeur compter de la méthode =)Je ne dirais pas que le problème est votre effort pour gérer manuellement la valeur de la position, mais plutôt que StreamReader.ReadLine changements du flux de valeur de Position. Si vous marchez par le biais de votre code et de surveiller vos valeurs locales, vous verrez le flux des changements de position après chaque ReadLine (appel à 148 après la première).
MODIFIER
Il serait préférable de simplement changer le flux de la position directement plutôt que d'utiliser Cherchent
Le problème est que vous êtes le suivi de la position manuellement, mais ne tenant pas compte du fait que la position dans le fichier va être une ligne plus loin dans la après avoir lu cette ligne. Donc, vous devez soustraire que supplémentaires de lecture --- mais seulement si elle s'est réellement passé.
Si vous voulez vraiment faire de cette façon, alors au lieu de les garder
position
, d'obtenir la réelle fichier de position; ou de calculer l'absolu, la position dans le fichier à partir de la ligne numéro de l'annonce y chercher directement à la place de l'actuel de l'offset du fichier.// Dynamic positioning position = (lineNo - 1) * LineLength - position; fs.Seek(position, SeekOrigin.Current);
à// Absolute positioning position = (lineNo - 1) * LineLength; fs.Seek(position, SeekOrigin.Begin);
j'obtiens toujours le même résultat erroné.