2011-07-28 36 views
7

Tôi cần phải phân tích cú pháp và mã hóa thành định dạng thông điệp nhị phân cũ trong Java. Tôi bắt đầu bằng cách sử dụng DataOutputStream để đọc/ghi các kiểu nguyên thủy nhưng vấn đề tôi gặp phải là định dạng tin nhắn không sắp xếp độc đáo với các offset byte và bao gồm các cờ bit.Làm cách nào để phân tích/mã hóa các định dạng thông báo nhị phân?

Ví dụ tôi phải đối phó với những thông điệp như thế này:

+----------+---+---+----------+---------+--------------+ 
+uint32 +b +b + uint32 +4bit enum+32 byte string+ 
+----------+---+---+----------+---------+--------------+ 

đâu (b) là một lá cờ một chút. Vấn đề là các kiểu nguyên thủy java không liên kết với các ranh giới byte vì vậy tôi sẽ không thể sử dụng DataOutputStream để mã hóa điều này vì loại mức thấp nhất mà tôi có thể viết là một byte.

Có thư viện, chuẩn hoặc bên thứ ba nào để xử lý các định dạng thông báo mức bit tùy ý không?

Chỉnh sửa: Nhờ @Software Monkey vì buộc tôi phải xem thông số kỹ thuật của tôi chặt chẽ hơn. Spec tôi đang sử dụng thực sự sắp xếp trên ranh giới byte để DataOutputStream là thích hợp. Cho câu hỏi ban đầu của tôi mặc dù tôi đã đi với giải pháp được đề xuất bởi @emboss.

Chỉnh sửa: Mặc dù định dạng thông báo cho câu hỏi này được phát hiện là trên ranh giới byte Tôi đã gặp một định dạng thư khác có thể áp dụng cho câu hỏi gốc. Định dạng này định nghĩa một ánh xạ ký tự 6 bit trong đó mỗi ký tự thực sự chỉ chiếm 6 bit, không phải là byte đầy đủ, vì vậy các chuỗi ký tự không được căn chỉnh trên các ranh giới byte. Tôi đã phát hiện một số luồng đầu ra nhị phân giải quyết vấn đề này. Như thế này: http://introcs.cs.princeton.edu/java/stdlib/BinaryOut.java.html

+4

Đây là một rất không bình thường spec; với nhiều năm kinh nghiệm của tôi trong comms dây, tôi sẽ đặt câu hỏi liệu bạn có bố trí đó chính xác. Đặc biệt, uint32 thứ hai của bạn và chuỗi byte 32 bắt đầu giữa chừng thông qua một byte - chắc chắn là không! Nó không phải là bất thường để đóng gói cờ vào bit trong một byte, từ hoặc dword cho một giao thức dây, nhưng để có sai lệch đầy đủ byte, từ, và dwords là ngu ngốc. –

+0

Ack, bạn đã đúng. Tôi đã có một hình ảnh nghèo của các định dạng tin nhắn để đi ra khỏi và bây giờ tôi thấy 'phụ tùng bit' chia lưới giữa các cờ bit và nguyên thủy sắp xếp tiếp theo nó trên một ranh giới byte. Cảm ơn! – kenen

+0

Bạn nên cập nhật câu hỏi của mình với thông số thực tế (và ghi chú sao cho câu trả lời của hình nổi không nhìn ra ngoài). –

Trả lời

5

Có một BUILTIN byte loại trong Java, và bạn có thể đọc vào bộ đệm byte[] tốt sử dụng InputStream#read(byte[]) và ghi vào một OutputStream sử dụng OutputStream#write(byte[], int, int), vì vậy không có vấn đề trong đó.

Về thông điệp của bạn - khi bạn ghi nhận một cách chính xác, các bit nhỏ nhất của thông tin bạn nhận được tại một thời điểm là một byte, vì vậy bạn sẽ phải phân hủy định dạng thông điệp của bạn thành 8 khối bit đầu tiên:

Giả sử tin nhắn của bạn là một byte [] có tên dữ liệu. Tôi cũng cho rằng một chút ít kiên định.

Một uint32 dài 32 bit -> đó là bốn byte. (Hãy cẩn thận khi phân tích cú pháp này trong Java, các số nguyên và thời gian Java được ký, bạn cần phải xử lý điều đó. Một cách dễ dàng để tránh sự cố sẽ mất nhiều thời gian cho điều đó. [0] điền bit 31 - 24, dữ liệu [1] 23 - 16, dữ liệu [2] bit 15-8 và dữ liệu [3] bit 7 đến 0. Vì vậy, bạn cần phải thay đổi chúng một cách thích hợp để ở bên trái và dán chúng lại với nhau với logic OR:

long uint32 = ((data[0]&0xFF) << 24) | 
       ((data[1]&0xFF) << 16) | 
       ((data[2]&0xFF) << 8) | 
       (data[3]&0xFF); 

Tiếp theo, có hai bit đơn.Tôi cho rằng bạn phải kiểm tra xem chúng có "on" (1) hay "off" (0) Để làm điều này, bạn sử dụng bit mask và so sánh byte của bạn với logic AND.

chút Đầu tiên: (nhị phân nạ | 1 0 0 0 0 0 0 0 | = 128 = 0x80)

if ((data[4] & 0x80) == 0x80) // on 

chút Thứ hai: (nhị phân nạ | 0 1 0 0 0 0 0 0 | = 64 = 0x40)

if ((data[4] & 0x40) == 0x40) // on 

Để soạn uint32 tiếp theo, bạn sẽ phải soạn byte trên ranh giới byte của dữ liệu cơ bản. Ví dụ. đối với byte đầu tiên lấy 6 bit dữ liệu còn lại [4], dịch hai bit sang trái (chúng sẽ là bit 8 đến 2 của uint32) và "thêm" dữ liệu đầu tiên (cao nhất) hai [5] bằng cách dịch chuyển họ 6 bit bên phải (họ sẽ lấy 1 và 0 khe còn lại của uint32). "Đang thêm" có nghĩa là một cách hợp lý OR'ing:

byte uint32Byte1 = (byte)((data[4]&0xFF) << 2 | (data[5]&&0xFF) >> 6); 

Xây dựng uint32 của bạn sau đó cũng giống như trong ví dụ đầu tiên. Vân vân và vân vân.

+0

Mã trích xuất uint32 ở trên bị hỏng cho giá trị byte <0; bạn cần sử dụng '((dữ liệu [x] & 0xFF) << n)'; nếu không 'dữ liệu [x]' được chuyển thành int tương đương đã ký, *** trước khi *** được dịch chuyển, gây ra vùng đệm cao bit. Ngoại lệ là '<< 24' cho 32 bit và' << 56L' cho giá trị 64 bit, cả hai đều sẽ dịch chuyển bất kỳ dấu mở rộng nào. –

+0

@Software Monkey: Bạn nói đúng, cảm ơn! Tôi sẽ cập nhật bài đăng. – emboss

2

Bạn cần áp dụng bit arithmetics (AND, OR, AND NOT operator) để thay đổi hoặc đọc các bit đơn trong một byte trong Java. Toán tử số học là &, | và ~

+0

Nó hoạt động để lấy các cờ bit đầu tiên ở đầu byte. Điều đó vẫn sẽ để lại một số logic lạ để đối phó với nói một uint32 mà bắt đầu 4 bit thành một byte và tiếp tục cho 3,5 byte nhiều hơn nữa. – kenen

4

Tôi đã nghe những điều thú vị về Preon.

+0

Trang web này có vẻ khá bị hỏng; không thể nhận được bất kỳ ví dụ nào để đưa ra - chỉ hiển thị một sự xáo trộn và nội dung trống. –

+0

Không chắc chắn trang web nào bạn đã kiểm tra, nhưng với tôi, http://preon.codehaus.org/ dường như không bị hỏng. Bạn có thể muốn kiểm tra http://www.slideshare.net/springerw/oopsla-talk-on-preon cho các trang trình bày trên Preon và http://www.scribd.com/doc/8128172/Preon-Giới thiệu và liên quan đến thêm tài liệu. (Tôi đồng ý mặc dù mã ví dụ thực sự nên được bao gồm trong tài liệu được tạo bằng Maven.) –

4

Chỉ cần để thêm vào câu trả lời pholser, tôi nghĩ rằng phiên bản Preon sẽ là một cái gì đó như thế này:

class DataStructure { 
    @BoundNumber(size="32") long  first; // uint32 
    @Bound     boolean second; // boolean 
    @Bound     boolean third; // boolean 
    @BoundNumber(size="32") long  fourth; // uint32 
    @BoundNumber(size="4") int  fifth; // enum 
    @BoundString(size="32") String  sixth; // string 
} 

... nhưng trong thực tế, bạn có thể làm cho cuộc sống của bạn dễ dàng hơn bằng cách sử dụng hỗ trợ Preon của for dealing with enumerations directly.

Tạo một Codec cho nó và sử dụng nó để giải mã một số dữ liệu sẽ là một cái gì đó như thế này:

Codec<DataStructure> codec = Codecs.create(DataStructure.class) 
DataStructure data = Codecs.decode(codec, ....) 
4

với Java Binary Block Parser kịch bản để phân tích thông điệp sẽ

class Parsed { 
    @Bin int field1; 
    @Bin (type = BinType.BIT) boolean field2; 
    @Bin(type = BinType.BIT) boolean field3; 
    @Bin int field4; 
    @Bin(type = BinType.BIT) int enums; 
    @Bin(type = BinType.UBYTE_ARRAY) String str; 
    } 

    Parsed parsed = JBBPParser.prepare("int field1; bit field2; bit field3; int field4; bit:4 enums; ubyte [32] str;").parse(STREAM).mapTo(Parsed.class); 
Các vấn đề liên quan