Clojure:在Java字节数组中查找字节序列

时间:2017-01-13 03:31:54

标签: java arrays clojure

有人能给我一个提示如何在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图像中图像数据开始的位置(元数据部分停止)。

3 个答案:

答案 0 :(得分: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)))))))

在repl中:

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

答案 1 :(得分:0)

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)])

答案 2 :(得分:-1)

更新的解决方案

这是解决问题的更好方法。虽然我通常不喜欢使用php.ini,但我的意思是nil,内置函数false很好地将keep = true / false的决定与减少丢弃keep = false的所有值的步骤。

keep-indexed

原始解决方案

这是一种做法。使用(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)))) from the Tupelo library,您可以直观地查看步骤:

spy-let

结果:

(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))

不知道您的数据格式,只需将其放入Clojure向量中,然后不要担心已签名和无符号字节。