12
votes

Meilleure façon de trouver la position dans le flux où la séquence d'octets donnée commence

Comment pensez-vous quelle est la meilleure façon de trouver la position dans le système.stream où la séquence d'octets donnée commence (première occurrence):

public static long FindPosition(Stream stream, byte[] byteSequence)
{
    long position = -1;

    /// ???
    return position;
}


6 commentaires

Votre question est confuse ... que cherchez-vous? cette séquence spécifique d'octets dans le flux?


Je pense que le titre de la question devrait être mis à jour. Le flux est mal orthographié comme une vapeur, ce qui semble être une question qui doit être étiquetée de la vanne.


@chollida: En fait, je suis venu à cette question juste pour résoudre ce problème.


En fait, je cherche GUID dans le flux.


La mémoire est-elle un problème? Ou pouvez-vous lire tout le flux dans un tableau d'octets?


Veuillez vérifier si ma solution correspond à vos besoins.


6 Réponses :


3
votes

Vous devez essentiellement garder un tampon de la même taille que bytésequence de sorte qu'une fois que vous avez constaté que "l'octet suivant" dans le flux correspond, vous pouvez vérifier le reste, mais toujours Retournez à l'octet «Suivant mais un» si ce n'est pas une correspondance réelle.

Peu importe que ce soit un peu fidèle à ce que vous faites, d'être honnête: (


0 commentaires

4
votes

Si vous traitez le flux comme une autre séquence d'octets, vous pouvez simplement le rechercher comme si vous effectuiez une recherche de chaîne. Wikipedia a un excellent article à ce sujet. Boyer-Moore est un algorithme bon et simple pour cela.

Voici un piratage rapide que j'ai mis en place à Java. Ça marche et il est assez proche sinon Boyer-Moore. J'espère que cela aide;) xxx


2 commentaires

Ce n'est peut-être pas le plus simple, mais c'est assez rapide. Il pense que, étant donné que les contraintes de la lecture d'un flux ne permettent pas de simples si vous voulez une vitesse. Mais j'espère que mon code peut atténuer certains de vos problèmes ou d'aider à une personne à l'avenir.


Il semble initbufferSize variable dans FindBytes n'est pas utilisé.



5
votes

J'ai atteint cette solution.

J'ai fait des points de repère avec un fichier ASCII qui était 3.050 kb et 38803 lignes . Avec une recherche octet tableau de 22 octets Dans la dernière ligne du fichier, j'ai résultat sur 2.28 secondes (dans une machine lente / ancienne). xxx


2 commentaires

Pour une référence future, Padleft Séshergence recherche le premier octet non correspondant qui a provoqué séquenceequal pour renvoyer false. Cela ressemble à une micro-optimisation pour moi, car on pourrait s'attendre à ce que séquenceequal au début du retour d'un non-match de toute façon. Disclaimer: Je n'ai pas fait de mesures, ce n'est qu'une opinion.


Cela ne fonctionne-t-il que si la séquence est à l'index d'une multiplication de longueur? Je veux dire, 6 octets SEQ à l'index 4 ne seront pas trouvés?



0
votes

Peu importante question, mais voici ma réponse. J'ai découvert que les blocs de lecture et ensuite chercher ce qui est extrêmement inefficace par rapport à la lecture d'une à la fois et à partir de là.

Également, IIRC, la réponse acceptée échouerait si une partie de la séquence était en un bloc de lecture et de moitié dans une autre - ex, étant donné 12345, à la recherche de 23, il ne correspondait pas, ne correspond pas à 34, ne correspond pas à 34, pas de correspondance. , etc ... ne l'a pas essayé, cependant, voyant comme cela nécessite Net 4.0. En tout cas, cela est de façon plus simple et probablement beaucoup plus rapide. P>

static long ReadOneSrch(Stream haystack, byte[] needle)
{
    int b;
    long i = 0;
    while ((b = haystack.ReadByte()) != -1)
    {
        if (b == needle[i++])
        {
            if (i == needle.Length)
                return haystack.Position - needle.Length;
        }
        else
            i = b == needle[0] ? 1 : 0;
    }

    return -1;
}


1 commentaires

Votre code est incorrect. Pensez à HAYSTACK = [2,1,2,1,1], aiguille = [2,1,1]. Votre code renvoie -1, mais la réponse correcte est 2



3
votes

J'avais besoin de le faire moi-même, avait déjà commencé et je n'ai pas aimé les solutions ci-dessus. J'ai spécifiquement besoin de trouver où se termine la séquence de recherche-octet. Dans ma situation, j'ai besoin de transférer le courant jusqu'à la séquence d'octets. Mais vous pouvez utiliser ma solution pour cette question aussi:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System
{

    static class StreamExtensions
    {
        /// <summary>
        /// Advances the supplied stream until the given searchBytes are found, without advancing too far (consuming any bytes from the stream after the searchBytes are found).
        /// Regarding efficiency, if the stream is network or file, then MEMORY/CPU optimisations will be of little consequence here.
        /// </summary>
        /// <param name="stream">The stream to search in</param>
        /// <param name="searchBytes">The byte sequence to search for</param>
        /// <returns></returns>
        public static int ScanUntilFound(this Stream stream, byte[] searchBytes)
        {
            // For this class code comments, a common example is assumed:
            // searchBytes are {1,2,3,4} or 1234 for short
            // # means value that is outside of search byte sequence

            byte[] streamBuffer = new byte[searchBytes.Length];
            int nextRead = searchBytes.Length;
            int totalScannedBytes = 0;

            while (true)
            {
                FillBuffer(stream, streamBuffer, nextRead);
                totalScannedBytes += nextRead; //this is only used for final reporting of where it was found in the stream

                if (ArraysMatch(searchBytes, streamBuffer, 0))
                    return totalScannedBytes; //found it

                nextRead = FindPartialMatch(searchBytes, streamBuffer);
            }
        }

        /// <summary>
        /// Check all offsets, for partial match. 
        /// </summary>
        /// <param name="searchBytes"></param>
        /// <param name="streamBuffer"></param>
        /// <returns>The amount of bytes which need to be read in, next round</returns>
        static int FindPartialMatch(byte[] searchBytes, byte[] streamBuffer)
        {
            // 1234 = 0 - found it. this special case is already catered directly in ScanUntilFound            
            // #123 = 1 - partially matched, only missing 1 value
            // ##12 = 2 - partially matched, only missing 2 values
            // ###1 = 3 - partially matched, only missing 3 values
            // #### = 4 - not matched at all

            for (int i = 1; i < searchBytes.Length; i++)
            {
                if (ArraysMatch(searchBytes, streamBuffer, i))
                {
                    // EG. Searching for 1234, have #123 in the streamBuffer, and [i] is 1
                    // Output: 123#, where # will be read using FillBuffer next. 
                    Array.Copy(streamBuffer, i, streamBuffer, 0, searchBytes.Length - i);
                    return i; //if an offset of [i], makes a match then only [i] bytes need to be read from the stream to check if there's a match
                }
            }

            return 4;
        }

        /// <summary>
        /// Reads bytes from the stream, making sure the requested amount of bytes are read (streams don't always fulfill the full request first time)
        /// </summary>
        /// <param name="stream">The stream to read from</param>
        /// <param name="streamBuffer">The buffer to read into</param>
        /// <param name="bytesNeeded">How many bytes are needed. If less than the full size of the buffer, it fills the tail end of the streamBuffer</param>
        static void FillBuffer(Stream stream, byte[] streamBuffer, int bytesNeeded)
        {
            // EG1. [123#] - bytesNeeded is 1, when the streamBuffer contains first three matching values, but now we need to read in the next value at the end 
            // EG2. [####] - bytesNeeded is 4

            var bytesAlreadyRead = streamBuffer.Length - bytesNeeded; //invert
            while (bytesAlreadyRead < streamBuffer.Length)
            {
                bytesAlreadyRead += stream.Read(streamBuffer, bytesAlreadyRead, streamBuffer.Length - bytesAlreadyRead);
            }
        }

        /// <summary>
        /// Checks if arrays match exactly, or with offset. 
        /// </summary>
        /// <param name="searchBytes">Bytes to search for. Eg. [1234]</param>
        /// <param name="streamBuffer">Buffer to match in. Eg. [#123] </param>
        /// <param name="startAt">When this is zero, all bytes are checked. Eg. If this value 1, and it matches, this means the next byte in the stream to read may mean a match</param>
        /// <returns></returns>
        static bool ArraysMatch(byte[] searchBytes, byte[] streamBuffer, int startAt)
        {
            for (int i = 0; i < searchBytes.Length - startAt; i++)
            {
                if (searchBytes[i] != streamBuffer[i + startAt])
                    return false;
            }
            return true;
        }
    }
}


0 commentaires

0
votes
static long Search(Stream stream, byte[] pattern)
{
    long start = -1;

    stream.Seek(0, SeekOrigin.Begin);

    while(stream.Position < stream.Length)
    {
        if (stream.ReadByte() != pattern[0])
            continue;

        start = stream.Position - 1;

        for (int idx = 1; idx < pattern.Length; idx++)
        {
            if (stream.ReadByte() != pattern[idx])
            {
                start = -1;
                break;
            }
        }

        if (start > -1)
        {
            return start;
        }
    }

    return start;
}

1 commentaires

Bienvenue dans le débordement de pile. Essayez d'éviter la réponse du code uniquement et donnez une explication de votre code.