2009-09-24 18 views
9

Nasıl verilen bayt dizisi başladığı (Birinci oluşumu) System.Stream konumunu bulmanın en iyi yolu nedir sizce başlar PS En basit ama en hızlı çözüm tercih edilir. :)iyi yolu

+1

soru kafa karıştırıcı olduğunu: Ama sen de bu soru için benim çözüm kullanabilir? Akıştaki belirli bayt dizisi? – dharga

+0

Sanırım sorunun başlığı güncellenmeli. Stream, Vana olarak etiketlenmesi gereken bir soru gibi görünmesini sağlayan Steam olarak yanlış yazılmış. – chollida

+0

@chollida: Aslında, bunu düzeltmek için bu soruya geldim. – Powerlord

cevap

5

çözümdür.

3.050 KB ve 38803 lines olan bir ASCII dosyasıyla bazı karşılaştırmalar yaptım. Dosyanın son satırında bytearray22 bytes aramasında sonuç yaklaşık 2.28 saniye (yavaş/eski bir makinede) elde ettim.

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

Gelecekte başvurmak için 'PadLeftSequence',' SequenceEqual '' false değerini döndüren ilk eşleşen olmayan baytı arar. Benim için bir mikro-optimizasyon gibi görünüyor, çünkü birisi bir "SequenceEqual" 'ın bir maçtan erken geri dönmesini bekliyordu. Feragatname: Herhangi bir ölçüm yapmadım, bu sadece bir fikir. –

+1

sadece dizilim bir uzunluk çoğaltmasının dizinindeyse işe yaramaz mı? Dizin 4'te 6 baytlık bir sekme bulunmayacak mı? – YoniXw

3

Temel olarak, arabellek byteSequence ile aynı boyutta tutmanız gerekir, böylece akıştaki "sonraki bayt" öğesinin eşleştiğini gördükten sonra kalanını kontrol edebilirsiniz, ancak yine de geri dönebilirsiniz. gerçek bir eşleşme değilse "sonraki ama bir" bayt.

O biraz destek ne yaparsanız yapın olması olasıdır

, dürüst olmak gerekirse :(

4

Eğer bayt başka dizisi gibi akışını ele alırsanız, bir dize arama yaptıklarını gibi, sadece arama yapamaz. Wikipedia sahiptir Bunun için harika bir makale Boyer-Moore, bunun için iyi ve basit bir algoritmadır.

İşte, Java'da bir araya getirdiğim hızlı bir korsanlık. Çalışır ve Boyer-Moore olmasa da oldukça yakın. Umarım yardım eder;)

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

en basit olmayabilir, ancak oldukça hızlıdır. Bir akıştan okumakla ilgili kısıtlamaların, hız istediğinizde basit olmasına izin vermediğini düşünün.ama umarım benim kodum bazı sıkıntılarınızı hafifletebilir veya gelecekte birisine yardım edebilir. – dharga

2

Bit eski soru, ama işte benim cevabım. Okuma bloklarını buldum ve daha sonra arama yapmak, bir kerede sadece bir tane okumakla ve oradan gitmekle kıyaslandığında son derece verimsiz.

Ayrıca, IIRC, dizinin bir kısmı bir blok okumada ve yarısında bir diğerinde - ex, 12345'te 23'ü ararken 12, okuyamayacak, sonra okuyacak 34, kabul edilmez. maç, vs ... denemedim, net 4.0 gerektirdiği gibi. Her halükarda, bu daha basit ve daha hızlıdır.

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

kodunuz yanlış. haystack = [2,1,2,1,1], needle = [2,1,1]. Kodunuz -1 döndürür, ancak doğru cevap 2'dir. – imaximchuk

2

Bunu kendim yapmalıydım, daha önce başlamıştım ve yukarıdaki çözümleri beğenmedim. Özellikle arama-bayt dizisinin nerede bittiğini bulmam gerekiyordu. Benim durumumda, akışı bayt dizisinden sonraya kadar ileri sarmalıyım. Eğer aradığınızı ...

İşte
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; 
     } 
    } 
}