有没有一种简单的方法可以匹配字节数组中的字节数组?

Is there a simple method to match an array of bytes in an byte array?

我正在尝试检查字节数组中的字节序列。 ByteArray class 中有没有像 indexOf() 一样工作的方法?

例如,

我想知道文件是 PNG 还是 JPG,所以我想检查字节数组中的字符序列。

var PNG_INFO:Array = [89,50,4E,47];
var byteArray:ByteArray = new ByteArray();

var position:int = byteArray.indexOf(PNG_INFO);
var value:String = byteArray.readBytes(position, PNG_INFO.length);

if (value =="PNG") { trace("is png") }

我不知道上面的代码是否正确,但我一直运行解决这个问题,我必须在字节数组中找到一个字节数组 .所以我的问题是有没有一种方法可以满足我的需求?

有关 PNG Header 十六进制字符数组 here 的更多信息。

更新:
我刚才想我希望我可以使用正则表达式来找到我正在寻找的东西:

// dream code - find the value between those characters 
var data:Object = byteArray.exec(/89,50,4E,47(.*?)0xF0,0xF1/);

当然,那只是梦。如果 ByteArrays 支持 RegEx,那就太容易了。

根据 Nick 的回答,我需要:

...loop the byte array, grab each byte and if there's a match keep comparing until I either find the full match or the end of the file?

有什么方法可以帮我完成这部分吗?这就是我想知道的。这似乎是您必须经常做的事情,所以也许有功能。如果是这样的话,我可以 post 我到目前为止所写的内容。

PNG 和 JPG 都有指定的 header,并且它们都有特定的尺寸。您只需要读取 header 的字节(小心检查字节数组大小,以免超出范围)。对于 PNG,您首先检查是否有 4 个字节要读取,然后获取前 4 个字节,与 0x89、0x50、0x4E、0x47 序列进行比较。对于 JPG,前 3 个字节将为 0xFF 0xD8 0xFF。

另外请记住,AS3 加载程序 class 可以只获取其中任何一个的字节并为您计算出来。

我已将此方法放在一起,但它未能找到 PNG 数组中的第一个值,但它找到的其他值都很好。

var png:Array = [0x89,0x50,0x4E,0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 4);

/**
 * Gets the position where either a single character or an array of hexidecimal values are found in a byte array
 * */
public function getIndexOfValueInByteArray(byteArray:ByteArray, value:*, startPosition:int = 0, endPosition:int = 0, endian:String = null):int {
    var byte:uint;
    var byteString:String;
    var position:int;
    var matchIndex:int;
    var searchArray:Array;
    var searchByte:int;
    var searchByteString:String;
    var found:Boolean;
    var endOfFile:Boolean;
    var endIndex:uint;
    var debug:Boolean;
    var firstByte:uint;
    var firstByteString:String;
    var startIndex:uint;
    var searchArrayLength:int;
    var compareAsString:Boolean;

    debug = true;

    if (value is String) {
        searchArray = String(value).split("");
        compareAsString = true;
    }
    else {
        searchArray = ArrayUtil.toArray(value);
    }

    if (endian) {
        byteArray.endian = endian;
    }

    if (startPosition>-1) {
        byteArray.position = startPosition;
    }

    if (searchArray && searchArray.length) {
        firstByte = searchArray[0];
        firstByteString = compareAsString ? searchArray[0] : String.fromCharCode(firstByte);
        searchArrayLength = searchArray.length;
    }
    else {
        return -1;
    }

    while (byteArray.bytesAvailable) {
        byte = byteArray.readByte();

        if (!compareAsString && byte==firstByte) {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

            for (var j:int = 1; j < searchArrayLength; j++) {
                byte = byteArray.readByte();
                searchByte = searchArray[j];

                debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

                if (byte==searchByte) {
                    if (j==searchArrayLength-1) {
                        found = true;
                        matchIndex = byteArray.position;
                        startIndex = matchIndex - searchArrayLength;
                        endIndex = matchIndex;

                        debug ? trace("Match found at " + startIndex):void;

                        break;
                    }
                }

                if (byteArray.bytesAvailable==0) {
                    endOfFile = true;
                    break;
                }
            }
        }

        else if (compareAsString && String.fromCharCode(byte)==firstByteString) {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;

            for (j = 1; j < searchArrayLength; j++) {
                byteString = String.fromCharCode(byteArray.readByte());
                searchByteString = searchArray[j];

                debug ? trace("Byte:0x" + byte.toString(16) + " " + searchByteString):void;

                if (byteString==searchByteString) {
                    if (j==searchArrayLength-1) {
                        found = true;
                        matchIndex = byteArray.position;
                        startIndex = matchIndex - searchArrayLength;
                        endIndex = matchIndex;

                        debug ? trace("Match found at " + startIndex):void;

                        break;
                    }
                }

                if (byteArray.bytesAvailable==0) {
                    endOfFile = true;
                    break;
                }
            }
        }
        else {
            debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
        }

        if (found || endOfFile || (endPosition!=0 && byteArray.position>endPosition)) {
            break;
        }
    }

    if (found) {
        debug?trace("Found at position " + startIndex + ". It ends at " + endIndex):0;
    }
    else {
        debug?trace("Could not find what the value you're looking for in this here byte array"):0;
        matchIndex = -1;
    }

    return matchIndex;
}

var png:Array = [0x89, 0x50, 0x4E, 0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 5);

查出的值是:

Byte:0xffffff89
Byte:0x50
Byte:0x4e
Byte:0x47

如果我将 byte 设置为 int 而不是 uint,它会打印出:

Byte:0x-77
Byte:0x50
Byte:0x4e
Byte:0x47

当我使用 JPEG 并将 byte 设置为 uint 时,它会打印出以下值:

var jpg:Array = [0xFF, 0xD8, 0xFF];
var jpgIndex:int = getIndexOfValueInByteArray(byteArray, jpg, 0, 5);

Byte:0xffffffff
Byte:0xffffffd8
Byte:0xffffffff
Byte:0xffffffe0

它看起来与最后一个值匹配。

更新:
我将只传递 0xFFFFFF89 而不是 0x89。这似乎适用于 Mac。不知道如何或为什么。我更新了函数以打印出十六进制字符和字符串字符,如果它转换为字符串(有时它是空的)。

看看这个例子是否对你有帮助。这个想法是找到一个候选字节,然后检查后续字节是否构成 rest-of-search 项。因此对于字节 CA FE BA BE 我们只搜索字节 0xCA 并且每当找到一个(从许多字节中)我们然后检查它是否跟随字节 0xFE+0xBA+0xBE...

这是一个完整的学习计划...

你想要的主要功能是:

indexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int

其中 return 是搜索序列开头偏移量的 int。我输入了一个 checkImageFormat( bytes ); 函数,其中 bytes.jpg.png 字节。这将 return 一个 String 表示是否 "jpg" 或 "png".

package  
{

import flash.display.MovieClip;
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.utils.*;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
import flash.events.*;
import flash.net.URLStream;


public class Check_Bytes_Image_v1 extends MovieClip 
{
    public var bytes    :ByteArray = new ByteArray();
    public var stream   :URLStream = new URLStream();
    public var loader   :Loader = new Loader();

    public var formatFound : Boolean = false;
    public var str_Format : String = "";
    public var str_Search : String = "";
    public var tempStr : String = "";
    public var tempUInt : uint = 0;
    public var bytePos : int = 0;

    public var searchString:String = "";;
    public var searchArray:Array = new Array();

    public function Check_Bytes_Image_v1() 
    {
        //# add canvas
        addChild(loader);

        //# load image bytes via URLStream
        stream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress);
        stream.addEventListener(Event.COMPLETE, completeHandler);
        stream.load(new URLRequest("image1.png")); //image1.png
    }

    public function onStreamProgress(evt:ProgressEvent):void
    { evt.target.readBytes(bytes, bytes.length, evt.target.bytesAvailable); }

    public function completeHandler(evt:Event):void 
    {

        //bytes_toString(bytes); //if you want to check hex output

        //# update a string with format type
        str_Format = checkImageFormat( bytes );
        trace("Image format is  : " + str_Format );

        searchString = "89 50 4E 47"; //use any known sequential bytes in your Array
        bytePos = indexOfBytes( bytes, searchString );
        trace("bytes begin at : " + bytePos);

        bytes.position = 0; //reset to starting pos
        loader.loadBytes(bytes); //decode & display image
        trace("loaded image now...");
        //bytes.clear(); //empty the bytes
    }

    //# findBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int
    public function indexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int
    {
        str_Search = value;
        trace("str_Search v1 is " + str_Search);

        //# Clean up possible hex notation
        str_Search = str_Search.split("0x").join("");
        str_Search = str_Search.split(".").join("");
        str_Search = str_Search.split("/").join("");

        //# Clean up hex (remove non Alpha-Numeric, white spaces, new line, etc)
        str_Search = str_Search.replace(/\W+/g, ""); //# clear all non Alpha-Numeric chars
        str_Search.toUpperCase(); //# convert to uppercase
        trace("str_Search v2 is " + str_Search);

        tempUInt = 0; searchArray = []; //# resets

        //# convert input string into two-digits-per-slot array
        while ( tempUInt < str_Search.length) 
        {
            searchArray.push(str_Search.substring(tempUInt, Math.min(tempUInt+2, str_Search.length) ) );
            tempUInt += 2;
        }

        trace("searchArray length is : " + searchArray.length);

        //# search bytes for same hex vlues

        //# Resets for WHILE loop
        if ( startPosition != 0) { bytePos = startPosition; }
        else { bytePos = 0; }

        tempUInt = 0;

        while (byteArray.bytesAvailable)
        {
            //# safety check :  Stop at limit of bytes range
            if ( endPosition != 0 ) //# Don't go over end pos if not null
            {   
                if ( byteArray.position >= endPosition )
                { 
                    trace("reached end position of search : stopping at: " + byteArray.position );
                    break;
                }
            }
            else //# Don't go over total bytes length
            {
                if ( byteArray.position > (byteArray.length - searchArray.length) )
                { 
                    trace("reached ENDing bytes of search : stopping at: " + byteArray.position );
                    break;
                }
            } //# end safety check...

            //# Find search values
            byteArray.position = bytePos;
            tempStr = byteToHex( byteArray.readUnsignedByte() );
            tempUInt = 0; //reset for TRUE count

            if ( tempStr == searchArray[0] )
            {
                //# tempUInt to count how many times it was true
                //# if total true's == search array length then we found match

                //trace("-------------------------------------------");

                for (var i:int = 0; i <= searchArray.length-1; i++) 
                {
                    bytePos = byteArray.position-1;

                    if ( byteToHex( byteArray[bytePos+i] ) == searchArray[i] )
                    {
                        //trace("bytes [" +bytePos+ " + " +i+ "] is : " +  byteToHex( byteArray[bytePos+i] ) );
                        //trace("search array [" +i+ "] is : " + searchArray[i] );
                        tempUInt++; //# add +1 for each matching entry for both arrays
                    }

                }

                trace("tempUInt match is : " + tempUInt + " || Expected TRUE count : " + searchArray.length );

                if ( tempUInt == searchArray.length ) 
                { 
                    tempUInt = byteArray.position - 1;
                    trace("match FOUND for search at pos : " + tempUInt); 
                    break; 
                }
                else 
                { 
                    tempUInt = int(-1); 
                    trace("match NOT found for search... current pos : " + ( byteArray.position-1) );
                }

            }

            bytePos++;

        } //# end WHILE loop

        return tempUInt;
    }

    public function byteToHex ( input:uint ) : String
    {
        tempStr = input.toString(16); 
        if(tempStr.length < 2) { tempStr = "0" + tempStr; } //# zero padding if 1 char only

        return tempStr.toUpperCase();
    }

    public function checkImageFormat ( inputBA : ByteArray ) : String
    {
        //# If you need to READ as specific Endianness of format
        //# default in AS3 is BIG ENDIAN type
        //inputBA.endian = Endian.LITTLE_ENDIAN;
        //inputBA.endian = Endian.BIG_ENDIAN;

        formatFound = false; bytePos = 0;

        while( formatFound == false) //* keep going until BREAK action
        {
            if ( formatFound == true ){ trace("## Found format..."); break; }

            inputBA.position = bytePos; //* reset positions 
            tempUInt = inputBA.readUnsignedByte(); //* READ byte = check byte value as Int
            bytePos = inputBA.position; //* update Pos to new offset move caused by READ action

            //# Check for JPG (begins xFF xD8 xFF xE0)
            if (tempUInt == 0xFF) 
            { 
                inputBA.position = bytePos-1; //* go back one byte to include FF in next check
                tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect FF-D8-FF-E0)
                //trace ("tempUInt check : " + tempUInt);
                if (tempUInt == 0xFFD8FFE0 ) //* check IF EQUAL to expected value
                { 
                    inputBA.position += 2; //skip 2 bytes ahead to expected pos of JFIF
                    tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytes
                    if (tempUInt == 0x4A464946) //* if expected JFIF (as hex) value
                    {
                        str_Format = "jpg";
                        trace("found JPG start + JFIF header..."); formatFound = true;
                    }
                }
            } //# end Check for JPG

            //# Check for PNG (begins x89 x50 x4E x47)
            if (tempUInt == 0x89) 
            { 
                inputBA.position = bytePos-1; //* go back one byte to include 89 in next check
                tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect 89-50-4E-47)
                //trace ("tempUInt check : " + tempUInt);
                if (tempUInt == 0x89504E47 ) //* check IF EQUAL to expected value
                { 
                    tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytes
                    if (tempUInt == 0x0D0A1A0A) //* if also expected value
                    {
                        inputBA.position += 4; //skip 4 bytes ahead to expected pos of IHDR
                        tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytes

                        if (tempUInt == 0x49484452) //* if expected IHDR (as hex) value
                        {
                            str_Format = "png";
                            trace("found PNG start + IHDR header..."); formatFound = true;
                        }
                    }
                }
            } //# end Check for PNG

            //# Increment Pos for next byte value checking at top of While loop
            bytePos++;

        } //# end WHILE loop

        return str_Format;
    }

    public function bytes_toString ( ba:ByteArray ) : String
    {
        //trace("checking args : " + args);
        tempStr = "";   var str_Hex:String = ""; var len:uint = ba.length;
        ba.position = 0;

        for (var i:uint = 0; i < len; i++) 
        {
            tempStr=ba.readUnsignedByte().toString(16); 

            if(tempStr.length<2) { tempStr="0"+tempStr; } //# zero-padding if 1 char only
            str_Hex += tempStr ;
        }

        return str_Hex.toUpperCase();
    }

    public function bytes_toInt( ...args ) : int
    {
        var temp_conv_ba : ByteArray = new ByteArray();

        for (var i:uint = 0; i < args.length; i++) 
        { temp_conv_ba[i] = args[i]; }

        var int_fromBytes:int = int("0x" + bytes_toString(temp_conv_ba) );
        return int_fromBytes;
    }

} //# end class

} //# end package