2009-09-24 21 views
9

आपको क्या लगता है System.Stream में स्थिति का पता लगाने का सबसे अच्छा तरीका क्या है (पहला मामला) जहां दिए गए बाइट क्रम शुरू होता है:शुरू होता है

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

    /// ??? 
    return position; 
} 

पुनश्च सबसे सरल अभी तक सबसे तेज़ समाधान preffered है। :)

+1

अपने प्रश्न भ्रामक है है ... आप के लिए क्या देख रहे हैं? स्ट्रीम में बाइट्स का वह विशिष्ट अनुक्रम? – dharga

+0

मुझे लगता है कि प्रश्न का शीर्षक अद्यतन किया जाना चाहिए। स्ट्रीम स्टीम के रूप में गलत वर्तनी है, जो इसे एक प्रश्न की तरह प्रतीत होता है जिसे वाल्व टैग किया जाना चाहिए। – chollida

+0

@ चोलिडा: वास्तव में, मैं इसे ठीक करने के लिए इस प्रश्न पर आया था। – Powerlord

उत्तर

5

मैं वें पहुंच गया समाधान है

मैंने 3.050 KB और 38803 lines पर एक ASCII फ़ाइल के साथ कुछ मानक बनाए थे। फ़ाइल की आखिरी पंक्ति में bytearray22 bytes के साथ मुझे 2.28 सेकंड (धीमी/पुरानी मशीन में) परिणाम मिला है।

public static long FindPosition(Stream stream, byte[] byteSequence) 
{ 
    if (byteSequence.Length > stream.Length) 
     return -1; 

    byte[] buffer = new byte[byteSequence.Length]; 

    using (BufferedStream bufStream = new BufferedStream(stream, byteSequence.Length)) 
    { 
     int i; 
     while ((i = bufStream.Read(buffer, 0, byteSequence.Length)) == byteSequence.Length) 
     { 
      if (byteSequence.SequenceEqual(buffer)) 
       return bufStream.Position - byteSequence.Length; 
      else 
       bufStream.Position -= byteSequence.Length - PadLeftSequence(buffer, byteSequence); 
     } 
    } 

    return -1; 
} 

private static int PadLeftSequence(byte[] bytes, byte[] seqBytes) 
{ 
    int i = 1; 
    while (i < bytes.Length) 
    { 
     int n = bytes.Length - i; 
     byte[] aux1 = new byte[n]; 
     byte[] aux2 = new byte[n]; 
     Array.Copy(bytes, i, aux1, 0, n); 
     Array.Copy(seqBytes, aux2, n); 
     if (aux1.SequenceEqual(aux2)) 
      return i; 
     i++; 
    } 
    return i; 
} 
+0

भविष्य के संदर्भ के लिए, 'PadLeftSequence' पहले गैर-मिलान बाइट की खोज कर रहा है जिसके कारण' अनुक्रम Equal' झूठी वापसी हुई। यह मेरे लिए एक माइक्रो-ऑप्टिमाइज़ेशन जैसा प्रतीत होता है, क्योंकि कोई भी 'अनुक्रम एक्वाल' की अपेक्षा करता है कि किसी भी मैच में किसी भी तरह की वापसी हो। अस्वीकरण: मैंने कोई माप नहीं किया है, यह केवल राय है। –

+1

यह केवल तभी काम करता है जब अनुक्रम लंबाई गुणा के सूचकांक पर है? मेरा मतलब है, सूचकांक 4 पर 6 बाइट्स सीक नहीं मिलेगा? – YoniXw

3

आपको मूल रूप से byteSequence के रूप में एक बफर रखने की आवश्यकता होगी ताकि एक बार जब आप पाएंगे कि स्ट्रीम मैचों में "अगला बाइट" है, तो आप बाकी की जांच कर सकते हैं लेकिन फिर भी वापस जा सकते हैं "अगला लेकिन एक" बाइट अगर यह वास्तविक मिलान नहीं है।

यह थोड़ा बारीकियों जो कुछ भी आप कर जाने की संभावना है, ईमानदार होना :(

4

आप बाइट्स की एक और अनुक्रम की तरह धारा का इलाज हैं, तो आप बस इसे खोज सकते हैं जैसे आप एक स्ट्रिंग खोज कर रहे थे। Wikipedia है उस पर एक महान लेख। बॉयर-मूर इस के लिए एक अच्छा और सरल एल्गोरिदम है।

जावा में एक त्वरित हैक मैंने एक साथ रखा है। यह काम करता है और बॉयर-मूर नहीं होने पर यह बहुत करीब है। उम्मीद है कि यह मदद करता है;)

public static final int BUFFER_SIZE = 32; 

public static int [] buildShiftArray(byte [] byteSequence){ 
    int [] shifts = new int[byteSequence.length]; 
    int [] ret; 
    int shiftCount = 0; 
    byte end = byteSequence[byteSequence.length-1]; 
    int index = byteSequence.length-1; 
    int shift = 1; 

    while(--index >= 0){ 
     if(byteSequence[index] == end){ 
      shifts[shiftCount++] = shift; 
      shift = 1; 
     } else { 
      shift++; 
     } 
    } 
    ret = new int[shiftCount]; 
    for(int i = 0;i < shiftCount;i++){ 
     ret[i] = shifts[i]; 
    } 
    return ret; 
} 

public static byte [] flushBuffer(byte [] buffer, int keepSize){ 
    byte [] newBuffer = new byte[buffer.length]; 
    for(int i = 0;i < keepSize;i++){ 
     newBuffer[i] = buffer[buffer.length - keepSize + i]; 
    } 
    return newBuffer; 
} 

public static int findBytes(byte [] haystack, int haystackSize, byte [] needle, int [] shiftArray){ 
    int index = needle.length; 
    int searchIndex, needleIndex, currentShiftIndex = 0, shift; 
    boolean shiftFlag = false; 

    index = needle.length; 
    while(true){ 
     needleIndex = needle.length-1; 
     while(true){ 
      if(index >= haystackSize) 
       return -1; 
      if(haystack[index] == needle[needleIndex]) 
       break; 
      index++; 
     } 
     searchIndex = index; 
     needleIndex = needle.length-1; 
     while(needleIndex >= 0 && haystack[searchIndex] == needle[needleIndex]){ 
      searchIndex--; 
      needleIndex--; 
     } 
     if(needleIndex < 0) 
      return index-needle.length+1; 
     if(shiftFlag){ 
      shiftFlag = false; 
      index += shiftArray[0]; 
      currentShiftIndex = 1; 
     } else if(currentShiftIndex >= shiftArray.length){ 
      shiftFlag = true; 
      index++; 
     } else{ 
      index += shiftArray[currentShiftIndex++]; 
     }   
    } 
} 

public static int findBytes(InputStream stream, byte [] needle){ 
    byte [] buffer = new byte[BUFFER_SIZE]; 
    int [] shiftArray = buildShiftArray(needle); 
    int bufferSize, initBufferSize; 
    int offset = 0, init = needle.length; 
    int val; 

    try{ 
     while(true){ 
      bufferSize = stream.read(buffer, needle.length-init, buffer.length-needle.length+init); 
      if(bufferSize == -1) 
       return -1; 
      if((val = findBytes(buffer, bufferSize+needle.length-init, needle, shiftArray)) != -1) 
       return val+offset; 
      buffer = flushBuffer(buffer, needle.length); 
      offset += bufferSize-init; 
      init = 0; 
     } 
    } catch (IOException e){ 
     e.printStackTrace(); 
    } 
    return -1; 
} 
+0

यह सबसे आसान नहीं हो सकता है, लेकिन यह बहुत तेज़ है। ऐसा लगता है कि यदि आप गति चाहते हैं तो स्ट्रीम से पढ़ने की बाधाओं को सरल बनाने की अनुमति नहीं दी जाती है।लेकिन मुझे उम्मीद है कि मेरा कोड आपकी कुछ परेशानियों को कम कर सकता है, या भविष्य में किसी के लिए मदद कर सकता है। – dharga

2

बिट पुराना सवाल, लेकिन यह मेरा जवाब है। मैंने पाया है कि ब्लॉक पढ़ना और फिर उसमें खोज करना एक समय में केवल पढ़ने और वहां से जाने की तुलना में बेहद अक्षम है।

इसके अलावा, आईआईआरसी, स्वीकार्य उत्तर विफल हो जाएगा यदि अनुक्रम का हिस्सा एक ब्लॉक में पढ़ा गया था और दूसरे में आधा - पूर्व, 12345 दिया गया, 23 की खोज कर रहा था, यह 12 पढ़ेगा, मैच नहीं करेगा, फिर 34 पढ़ेगा, मैच, इत्यादि ... ने कोशिश नहीं की है, हालांकि, इसे देखकर नेट 4.0 की आवश्यकता है। किसी भी दर पर, यह आसान तरीका है, और संभवतः बहुत तेज़ है।

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

आपका कोड गलत है। हैस्टैक = [2,1,2,1,1], सुई = [2,1,1] पर विचार करें। आपका कोड -1 देता है, लेकिन सही उत्तर 2 है – imaximchuk

2

मुझे इसे स्वयं करने की ज़रूरत है, पहले ही शुरू हो चुका है, और ऊपर दिए गए समाधान पसंद नहीं आया। मुझे विशेष रूप से यह पता लगाने की आवश्यकता है कि खोज-बाइट-अनुक्रम समाप्त होता है। मेरी स्थिति में, मुझे उस बाइट अनुक्रम के बाद तक स्ट्रीम को तेज़-आगे करने की आवश्यकता है। लेकिन आप भी इस प्रश्न के लिए मेरे समाधान का उपयोग कर सकते हैं:

var afterSequence = stream.ScanUntilFound(byteSequence); 
var beforeSequence = afterSequence - byteSequence.Length; 

यहाँ StreamExtensions.cs

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; 
     } 
    } 
} 
संबंधित मुद्दे