2009-01-29 48 views
6

Tôi có một mảng byte mà tôi đang đọc từ một NetworkStream. Hai byte đầu tiên cho biết chiều dài của gói tin sau và sau đó gói được đọc vào một mảng byte có độ dài đó. Dữ liệu mà tôi cần đọc từ mảng NetworkStream/byte có một vài Chuỗi, tức là dữ liệu độ dài biến được kết thúc bởi các ký tự dòng mới và một số trường có chiều rộng cố định như byte và độ dài. Vì vậy, một cái gì đó như thế này:Đọc dòng từ mảng byte (không chuyển đổi mảng byte thành chuỗi)

// I would have delimited these for clarity but I didn't want 
// to imply that the stream was delimited because it's not. 
StringbyteStringStringbytebytebytelonglongbytelonglong 

tôi biết (và có tiếng nói trong) định dạng của các gói dữ liệu được sắp qua, và những gì tôi cần làm là đọc một "dòng" cho mỗi chuỗi giá trị, nhưng đọc số byte cố định cho các byte và thời lượng. Cho đến nay, giải pháp được đề xuất của tôi là sử dụng vòng lặp while để đọc các byte thành một mảng byte tạm thời cho đến khi có một ký tự dòng mới. Sau đó, chuyển đổi các byte thành một chuỗi. Điều này có vẻ kludgy với tôi, nhưng tôi không thấy một cách rõ ràng. Tôi nhận ra tôi có thể sử dụng StreamReader.ReadLine() nhưng điều đó sẽ liên quan đến một luồng khác và tôi đã có một NetworkStream. Nhưng nếu đó là giải pháp tốt hơn, tôi sẽ cho nó một shot. Một tùy chọn khác mà tôi đã xem xét là để nhóm phụ trợ của tôi viết một hoặc hai byte cho độ dài của các giá trị String đó để tôi có thể đọc được độ dài và sau đó đọc Chuỗi dựa trên độ dài được chỉ định. Vì vậy, như bạn có thể thấy, tôi có một số tùy chọn để tìm hiểu về điều này và tôi muốn ý kiến ​​của bạn về những gì bạn sẽ xem là cách tốt nhất để thực hiện điều đó. Đây là mã mà tôi có ngay bây giờ để đọc trong toàn bộ gói dưới dạng chuỗi. Bước tiếp theo là phá vỡ các lĩnh vực khác nhau của gói và thực hiện công việc lập trình thực tế cần được thực hiện, tạo đối tượng, cập nhật giao diện người dùng, vv dựa trên dữ liệu trong gói.

string line = null; 
while (stream.DataAvailable) 
{ 
    //Get the packet length; 
    UInt16 packetLength = 0; 
    header = new byte[2]; 
    stream.Read(header, 0, 2); 
    // Need to reverse the header array for BitConverter class if architecture is little endian. 
    if (BitConverter.IsLittleEndian) 
     Array.Reverse(header); 
    packetLength = BitConverter.ToUInt16(header,0); 

    buffer = new byte[packetLength]; 
    stream.Read(buffer, 0, BitConverter.ToUInt16(header, 0)); 
    line = System.Text.ASCIIEncoding.ASCII.GetString(buffer); 
    Console.WriteLine(line); 
} 

Trả lời

9

Cá nhân tôi sẽ

  1. Đặt một Int16 vào lúc bắt đầu của chuỗi, vì vậy bạn biết bao lâu họ sẽ được, và
  2. Sử dụng lớp IO.BinaryReader để đọc, nó sẽ "đọc", ints, chuỗi, ký tự, v.v ... thành ví dụ BinReader.ReadInt16() sẽ đọc hai byte, trả lại int16 mà chúng đại diện và di chuyển hai byte vào trong luồng

Hy vọng điều này sẽ hữu ích.

P.S. Hãy cẩn thận khi sử dụng phương thức ReadString, nó giả định chuỗi được thêm vào với các số nguyên 7 bit tùy chỉnh, tức là nó được viết bởi lớp BinaryWriter. Sau đây là từ CodeGuru bài này

Các lớp BinaryWriter có hai phương pháp để viết chuỗi: phương pháp quá tải Write() và WriteString() phương pháp. Trước đây viết chuỗi dưới dạng luồng byte theo mã hóa mà lớp đang sử dụng. Phương thức Write2() cũng sử dụng mã hóa được chỉ định , nhưng nó tiền tố luồng byte của chuỗi với độ dài thực tế của chuỗi là . Các chuỗi tiền tố được đọc trước đó được đọc lại thông qua BinaryReader.ReadString().

Điều thú vị về giá trị chiều dài nó rằng càng ít càng tốt byte được sử dụng để giữ kích thước này, nó là lưu trữ như một loại gọi là 7-bit số nguyên được mã hóa. Nếu chiều dài phù hợp trong 7 bit một byte được sử dụng, nếu nó là lớn hơn này thì bit cao trên byte đầu tiên được đặt và giây thứ hai được tạo bằng cách dịch chuyển giá trị theo 7 bit. Điều này được lặp lại với byte liên tiếp cho đến khi có đủ byte để giữ giá trị. Cơ chế này được sử dụng để đảm bảo rằng độ dài không trở thành phần quan trọng của kích thước được chụp bằng chuỗi được xê-ri hóa. BinaryWriter và BinaryReader có các phương thức để đọc và ghi 7 bit số nguyên được mã hóa, nhưng chúng được bảo vệ và do đó bạn chỉ có thể sử dụng chúng nếu bạn lấy được từ các lớp này.

+0

Chưa từng gặp BinaryReader trước đây. Có vẻ như nó sẽ vô cùng hữu ích cho tất cả những thứ tôi đang làm. Cảm ơn! – jxpx777

+0

Yah, BinaryReader không hoạt động đối với chúng tôi vì dữ liệu đang được ghi vào luồng bởi ứng dụng Java. Tôi đã kết thúc "brute buộc" nó bằng cách đọc trong mảng byte và sau đó nguyền rủa thông qua nó dựa trên cấu trúc gói dữ liệu của chúng tôi. – jxpx777

5

Tôi sẽ đi với các chuỗi có độ dài tiền tố. Nó sẽ làm cho cuộc sống của bạn đơn giản hơn rất nhiều và điều đó có nghĩa là bạn có thể biểu diễn các chuỗi có ngắt dòng. Một vài nhận xét về mã của bạn mặc dù:

  • Không sử dụng Stream.DataAvailable. Chỉ vì không có sẵn dữ liệu hiện tại không có nghĩa là bạn đã đọc phần cuối của luồng.
  • Trừ khi bạn hoàn toàn chắc chắn bạn sẽ không bao giờ cần văn bản vượt quá ASCII, không sử dụng ASCIIEncoding.
  • Đừng cho rằng Stream.Read sẽ đọc tất cả dữ liệu bạn yêu cầu. Luôn luôn kiểm tra giá trị trả về.
  • BinaryReader giúp dễ dàng hơn rất nhiều (bao gồm các chuỗi có tiền dài và đọc vòng lặp đó cho đến khi nó đọc những gì bạn đã yêu cầu)
  • Bạn đang gọi BitConverter.ToUInt16 hai lần trên cùng một dữ liệu. Tại sao?
Các vấn đề liên quan