【问题标题】:How to search for a unique sequence in binary data?如何在二进制数据中搜索唯一序列?
【发布时间】:2017-11-18 02:23:56
【问题描述】:

我正在尝试读取带有标题的二进制文件。我知道某些信息是在唯一序列 02 06 08 22 02 02 08 00 之后保存的。我怎样才能找到这种唯一序列的位置?

我可以使用

String StreamReadAsText(ScriptObject stream, Number encoding, Number count)

逐一读取二进制文件。但我想这很愚蠢而且很慢。

此外,当输出不是实际文本(Ascii 表中的 00 和 1F 之间)时,我如何比较 StreamReadAsText() 的结果?

然后,我如何将二进制文件读取为 int8(与字符串中的字符大小相同)。例如,读取 02,然后是 06,然后是 08 等...

欢迎和感谢任何帮助。

问候,

罗杰

【问题讨论】:

标签: file search binary header dm-script


【解决方案1】:

您已经在使用流命令读取文件的正确轨道上。但是,为什么要将流作为文本读取?您可以将流读取为任何(支持的)数字,使用 tagGroup 对象作为TagGroupReadTagDataFromStream() 的代理。

F1 帮助部分实际上有一个示例,其中列出了流式命令,我只是在这里复制。

 Object stream = NewStreamFromBuffer( NewMemoryBuffer( 256 ) )
 TagGroup tg = NewTagGroup();

 Number stream_byte_order = 1; // 1 == bigendian, 2 == littleendian
 Number v_uint32_0, v_uint32_1, v_sint32_0, v_uint16_0, v_uint16_1

 // Create the tags and initialize with default values
 tg.TagGroupSetTagAsUInt32( "UInt32_0", 0 )
 tg.TagGroupSetTagAsUInt32( "UInt32_1", 0 )
 tg.TagGroupSetTagAsLong( "SInt32_0", 0 )
 tg.TagGroupSetTagAsUInt16( "UInt16_0", 0 )
 tg.TagGroupSetTagAsUInt16( "UInt16_1", 0 )

 // Stream the data into the tags   
 TagGroupReadTagDataFromStream( tg, "UInt32_0", stream, stream_byte_order );
 TagGroupReadTagDataFromStream( tg, "UInt32_1", stream, stream_byte_order );
 TagGroupReadTagDataFromStream( tg, "SInt32_0", stream, stream_byte_order );
 TagGroupReadTagDataFromStream( tg, "UInt16_0", stream, stream_byte_order );
 TagGroupReadTagDataFromStream( tg, "UInt16_1", stream, stream_byte_order );

// Show the taggroup, if you want
// tg.TagGroupOpenBrowserWindow("AuxTags",0)

 // Get the data from the tags
 tg.TagGroupGetTagAsUInt32( "UInt32_0", v_uint32_0 )
 tg.TagGroupGetTagAsUInt32( "UInt32_1", v_uint32_1 )
 tg.TagGroupGetTagAsLong( "Sint32_0", v_sint32_0 )
 tg.TagGroupGetTagAsUInt16( "UInt16_0", v_uint16_0 )
 tg.TagGroupGetTagAsUInt16( "UInt16_1", v_uint16_1 )

这里已经有一篇关于在流中搜索模式的帖子:Find a pattern image (binary file) 这显示了您将如何使用流查看图像,但您当然可以直接使用文件流。


作为替代方案,您可以在事先准备好合适的图像后使用ImageReadImageDataFromStream 从流中读取整个数组。 然后,您可以使用图像来搜索位置。这就是一个例子:

// Example of reading the first X bytes of a file
// as uInt16 data

image ReadHeaderAsUint16( string filepath, number nBytes )
{
    number kEndianness = 0 // Default byte order of the current platform
    if ( !DoesFileExist( filePath ) ) 
        Throw( "File '" + filePath + "' not found." )
    number fileID = OpenFileForReading( filePath )
    object fStream = NewStreamFromFileReference( fileID, 1 )
    if ( nBytes > fStream.StreamGetSize() ) 
        Throw( "File '" + filePath + "' has less than " + nBytes + "bytes." )

    image buff := IntegerImage( "Header", 2, 0, nBytes/2 )  // UINT16 array of suitable size
    ImageReadImageDataFromStream( buff, fStream, kEndianness )
    return buff 
}

number FindSignature( image header, image search )
{
    // 1D images only
    if (        ( header.ImageGetNumDimensions() != 1 ) \
            ||  ( search.ImageGetNumDimensions() != 1 ) )
        Throw( "Only 1D images supported" )

    number sx = search.ImageGetDimensionSize( 0 ) 
    number hx = header.ImageGetDimensionSize( 0 )
    if ( hx < sx )
        return -1

    // Create a mask of possible start locations
    number startV = search.getPixel( 0, 0 )
    image mask = (header == startV) ? 1 : 0

    // Search all the occurances from the first
    number mx, my
    while( max( mask, mx, my ) )
    {
        if ( 0 == sum( header[0,mx,1,mx+sx] - search ) )
            return mx
        else
            mask.SetPixel( mx, 0, 0)
    }
    return -1
}

// Example
// 1) Load file header as image (up to the size you want )
string path = GetApplicationDirectory( "open_save", 0 )
number maxHeaderSize = 200
if ( !OpenDialog( NULL, "Select file to open", path, path ) ) Exit(0)
image headerImg := ReadHeaderAsUint16( path, maxHeaderSize  )
headerImg.ShowImage()

// 2) define search-header as image
image search := [8]: { 02, 06, 08, 22, 02, 02, 08, 00 }
// MatrixPrint( search )

// 3) search for it in the header
number foundAt = FindSignature( headerImg, search )
if ( -1 == foundAt ) 
    Throw( "The file header does not contain the search pattern." )
else
    OKDialog( "Found the search pattern at offset: " + foundAt * 16 + "bytes" )

【讨论】:

  • 感谢您的回复。
  • 感谢您的回复。我对标签不是很熟悉。但我可以通过 Int32、Int16 和 Double 等标签读取流。据我了解,Int32 的长度类似于 02 06 08 22,而 Int16 的长度类似于 02 06。如果流的长度类似于 00 02 06 08,那么它会通过读取 Int16 错过这个序列。所以我需要读作“Int8”(我什至不知道它是否这样称呼)。那么长度应该只有02这样。这样我可以确保它不会错过正确的标题序列。现在的问题是我无法阅读“Int8”。仅读取流,因为文本具有这样的长度,但我无法比较 ASCII
  • @Roger 您可以将数据读取为 UInt8 图像 - IntegerImage(" ",1,0,...) - 或 Int8 图像 - IntegerImage("", 1, 1, ...) .或者您可以搜索两遍(每一步有 1 个字节的偏移量)。不幸的是,我现在不知道类似“GetTagAsUInt8”或“Int8”的命令。
  • 好主意!我正在将标题读取为 IntegerImage ("", 1, 0, nBytes)。使用 FindSignature 函数查找序列(这次是 8 像素的图像)。我用序列 {2,6,8,34,2,2,8,0} 进行了测试,效果很好。它找到了这样的序列的位置!!它也适用于序列 {112,0,7,5,1,96,0,34,7}。但是当我用序列 {0,7,5,1,96,0,34,7} 对其进行测试时,它并没有回到正确的位置。我想知道要查找的这个序列是否必须以非零值开头?另外,我可以从一个长字符串中以同样的方式搜索一个子字符串吗?
  • @Roger:你可以将这些东西用于字符串,但对于搜索子字符串,我宁愿使用前言“查找”命令。
【解决方案2】:

如果您在现代机器上,只需将文件加载到内存中,然后使用内存比较函数和移动索引扫描序列。

这不是最节省内存的方式,甚至不是最快的方式,但它足够简单和快速,前提是你有资源可以消耗。

【讨论】:

  • 不幸的是,在 DM 脚本语言中没有这样的功能可用。但是,我在下面发布的想法在原理上并没有太大区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-14
相关资源
最近更新 更多