【问题标题】:Clojure: Find Byte Sequence in Java Byte ArrayClojure:在 Java 字节数组中查找字节序列
【发布时间】:2017-01-13 03:31:54
【问题描述】:

谁能给我一个提示,如何在 java 字节数组中找到两个字节的序列。我正在寻找的两个字节是:FF DA(十六进制) 看起来 java 将一个字节的范围映射到 -127 到 128。我本来希望有一个 0 - 255 的范围。

这是一个建议:

(->> (partition 2 byte-array)
     (keep-indexed (fn [i ab]
                     (when (= ab [(byte 0xFF) (byte 0xDA)]))))
     first)

但是由于负范围,这不起作用。再加上使用这些高级函数和惰性序列的开销很可能不在这里。

我的实际用例是在 JPG 图像的字节数组中查找图像数据开始的位置(元数据部分停止)。

【问题讨论】:

    标签: java arrays clojure


    【解决方案1】:

    我会用这样的东西将数字转换为字节:

    user> 
    (defn as-byte [^long n]
      {:pre [(<= 0 n 255)]}
      ^byte (unchecked-subtract n 256))
    #'user/as-byte
    
    user> (as-byte 0xff)
    ;;=> -1
    
    user> (as-byte 0xda)
    ;;=> -38
    
    user> (as-byte 0xfff)
    AssertionError Assert failed: (<= 0 n 255)  user/as-byte (form-init2939145917481178115.clj:259)
    

    然后我会重写搜索函数,对数组而不是序列进行操作,因为它应该更快:

    (defn find-bytes-idx [^bytes look-for ^bytes data]
      (let [search-len (alength look-for)
            diff (- (alength data) search-len)]
        (if (neg? diff)
          -1
          (loop [i 0]
            (cond (> i diff) -1
                  (Arrays/equals look-for (Arrays/copyOfRange data i (+ i search-len))) i
                  :else (recur (inc i)))))))
    

    在回复中:

    user> (find-bytes-idx (byte-array (map as-byte [0xcd 0xef]))
                          (byte-array (map as-byte [0xaa 0xab 0xcd 0xcd 0xef 0xdf])))
    
    ;=> 3
    user> (find-bytes-idx (byte-array (map as-byte [0xcd 0xef]))
                          (byte-array (map as-byte [0xaa 0xab 0xcd 0xcd 0xee])))
    
    ;=> -1
    

    【讨论】:

      【解决方案2】:

      unchecked-byte 将强制转换为字节。或者,您可以将您的十六进制转换为有符号字节(-128 到 127)

      (unchecked-byte 0xff)
      ;;=> -1
      (unchecked-byte 0xda)
      ;;=> -38
      

      (->>
       (byte-array (map unchecked-byte
                        [0x07 0x00 0x05 0x06
                         0x07 0xE9 0xFF 0xDA
                         0x01 0x02 0x03 0x2A
                         0x33 0x99 0xFF 0xDA]))
                   (partition 2)
                   (keep-indexed (fn [i ab]
                                   (when
                                       (= ab [(unchecked-byte 0xFF)
                                              (unchecked-byte 0xDA)])
                                       [i ab]))))
      
      ;;=> ([3 (-1 -38)] [7 (-1 -38)])
      

      【讨论】:

        【解决方案3】:

        更新的解决方案

        这是解决问题的更好方法。虽然我通常不喜欢使用nil,但我的意思是false,内置函数keep-indexed 很好地结合了keep=true/false 的决定和丢弃所有keep=false 的值。

        (s/defn find-patterns :- [s/Int]
          [pattern-vec :- tsk/List
           data-vec :- tsk/List]
          (let [parts         (partition (count pattern-vec) 1 data-vec)
                idxs          (keep-indexed
                                (fn [idx candidate]
                                  (when (= candidate pattern-vec)
                                    idx))
                                parts)]
            idxs))
        
        (s/defn find-pattern :- s/Int
          [pattern  :- tsk/List
           data     :- tsk/List ]
          (first (find-patterns pattern data)))
        
        (deftest t-find-pattern
        ;              0 1 2  3    4    5    6    7   8 9]
          (let [data [ 0 1 2 0xAA 0xFA 0xFF 0xDA 0xDD 8 9] ]
            (is= 5 (find-pattern  [0xFF 0xDA] data))))
        

        原方案

        这是一种方法。使用spy-letfrom the Tupelo library,您可以直观地看到这些步骤:

        (ns tst.clj.core
          (:require [tupelo.core :as t]))
        (t/refer-tupelo)
        
        ;           0 1 2  3    4    5    6    7   8 9]
        (def data [ 0 1 2 0xAA 0xFA 0xFF 0xDA 0xDD 8 9] )
        
        (defn find-pattern
          [pattern data]
          (spy-let [
            patt-matches?   (fn [idx tst-pat] [idx (= tst-pat pattern) ] )
            parts           (partition (count pattern) 1 data)
            idx-labelled    (map-indexed patt-matches? parts)
            idx-matches?    (fn [[idx matches]] (= true matches))
            idx-that-match  (filter idx-matches? idx-labelled)
            result          (first (first idx-that-match))
          ]
            result
          )
        )
        (spyx (find-pattern [0xFF 0xDA] data))
        

        结果:

        parts => ((0 1) (1 2) (2 170) (170 250) (250 255) (255 218) (218 221) (221 8) (8 9))
        
        idx-labelled => ([0 false] [1 false] [2 false] [3 false] [4 false] [5 true] [6 false] [7 false] [8 false])
        
        idx-that-match => ([5 true])
        
        result => 5
        
        (find-pattern [255 218] data) => 5
        

        不知道您的数据格式,只需将其放入 Clojure 向量中,然后不用担心有符号字节与无符号字节。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-17
          • 1970-01-01
          • 2016-04-27
          • 2011-12-20
          • 2013-05-01
          相关资源
          最近更新 更多