Hiểu về tuần tự hóa


38

Tôi là một kỹ sư phần mềm và sau một cuộc thảo luận với một số đồng nghiệp, tôi nhận ra rằng tôi không nắm bắt được việc tuần tự hóa khái niệm. Theo tôi hiểu, tuần tự hóa là quá trình chuyển đổi một số thực thể, chẳng hạn như một đối tượng trong OOP, thành một chuỗi byte, để thực thể nói trên có thể được lưu trữ hoặc truyền đi để truy cập tiếp theo (quá trình "khử lưu huỳnh").

Vấn đề tôi gặp phải là: không phải tất cả các biến (có thể là nguyên thủy như inthoặc các đối tượng hỗn hợp) đã được biểu diễn bằng một chuỗi byte? (Tất nhiên là có, vì chúng được lưu trữ trong sổ đăng ký, bộ nhớ, đĩa, v.v.)

Vì vậy, những gì làm cho serialization như một chủ đề sâu sắc? Để tuần tự hóa một biến, chúng ta có thể lấy các byte này trong bộ nhớ và ghi chúng vào một tệp không? Những gì phức tạp tôi đã bỏ lỡ?


21
Tuần tự hóa có thể là tầm thường đối với các đối tượng tiếp giáp . Khi giá trị đối tượng được biểu diễn dưới dạng biểu đồ con trỏ , mọi thứ trở nên khó khăn hơn nhiều, đặc biệt nếu biểu đồ đã nói có các vòng lặp.
chi

1
@chi: Câu đầu tiên của bạn là một chút sai lệch được đưa ra liên tục là không liên quan. Bạn có thể có một biểu đồ xảy ra liên tục trong bộ nhớ và điều đó vẫn không giúp bạn sắp xếp nó vì bạn vẫn phải (a) phát hiện ra rằng nó thực sự xảy ra liền kề và (b) sửa các con trỏ bên trong. Tôi chỉ nói phần thứ hai của những gì bạn nói.
Mehrdad

@Mehrdad Tôi đồng ý nhận xét của tôi không hoàn toàn chính xác, vì những lý do bạn đề cập. Có lẽ không có con trỏ / sử dụng con trỏ là một sự khác biệt tốt hơn (ngay cả khi không hoàn toàn chính xác)
chi

7
Bạn cũng phải lo lắng về đại diện trên phần cứng. Nếu tôi tuần tự hóa một int 4 bytestrên PDP-11 của mình và sau đó thử và đọc bốn byte tương tự đó vào bộ nhớ trên macbook của tôi thì chúng không phải là cùng một số (vì Endianes). Vì vậy, bạn phải chuẩn hóa dữ liệu thành một đại diện mà bạn có thể giải mã (đây là tuần tự hóa). Cách bạn tuần tự hóa dữ liệu cũng có thể đánh đổi tốc độ / tính linh hoạt của con người / máy.
Martin York

Điều gì xảy ra nếu bạn đang sử dụng Entity Framework với nhiều thuộc tính điều hướng được kết nối sâu? Trong một trường hợp, bạn có thể muốn tuần tự hóa một thuộc tính điều hướng, nhưng trong một trường hợp khác, hãy để trống (vì bạn sẽ tải lại đối tượng thực tế đó từ cơ sở dữ liệu dựa trên ID trong đối tượng cha được tuần tự hóa của bạn). Đây chỉ là một ví dụ. Có nhiều.
ErikE

Câu trả lời:


40

Nếu bạn có cấu trúc dữ liệu phức tạp, biểu diễn của nó trong bộ nhớ thường có thể bị phân tán trong bộ nhớ. (Ví dụ, nghĩ về một cây nhị phân.)

Ngược lại, khi bạn muốn ghi nó vào đĩa, có lẽ bạn muốn có một biểu diễn dưới dạng một chuỗi (hy vọng ngắn) của các byte liền kề. Đó là những gì serialization làm cho bạn.


27

Vấn đề tôi gặp phải là: không phải tất cả các biến (có thể là nguyên thủy như các đối tượng int hoặc composite) đã được biểu diễn bằng một chuỗi byte? (Tất nhiên là có, vì chúng được lưu trữ trong sổ đăng ký, bộ nhớ, đĩa, v.v.)

Vì vậy, những gì làm cho serialization như một chủ đề sâu sắc? Để tuần tự hóa một biến, chúng ta có thể lấy các byte này trong bộ nhớ và ghi chúng vào một tệp không? Những gì phức tạp tôi đã bỏ lỡ?

Hãy xem xét một đồ thị đối tượng trong C với các nút được định nghĩa như sau:

struct Node {
    struct Node* parent;
    struct Node* someChild;
    struct Node* anotherLink;

    int value;
    char* label;
};

//

struct Node nodes[10] = {0};
nodes[5].parent = nodes[0];
nodes[0].someChild = calloc( 1, sizeof(struct Node) );
nodes[5].anotherLink = nodes[3];
for( size_t i = 3; i < 7; i++ ) {
    nodes[i].anotherLink = calloc( 1, sizeof(struct Node) );
}

Trong thời gian chạy, toàn bộ Nodeđồ thị đối tượng sẽ nằm rải rác xung quanh không gian bộ nhớ và cùng một nút có thể được trỏ đến từ nhiều Nút khác nhau.

Bạn không thể kết xuất bộ nhớ vào một tệp / luồng / đĩa và gọi nó là tuần tự vì các giá trị con trỏ (là địa chỉ bộ nhớ) không thể được tuần tự hóa (vì các vị trí bộ nhớ đó có thể đã bị chiếm dụng khi bạn tải lại kết xuất vào bộ nhớ). Một vấn đề khác với việc bỏ bộ nhớ đơn giản là cuối cùng bạn sẽ lưu trữ tất cả các loại dữ liệu không liên quan và dung lượng không sử dụng - trên x86, một quá trình có tới 4GiB không gian bộ nhớ và HĐH hoặc MMU chỉ có ý tưởng chung về bộ nhớ thực sự là gì có ý nghĩa hay không (dựa trên các trang bộ nhớ được gán cho một tiến trình), do đó, việc Notepad.exeđổ 4GB byte thô vào đĩa của tôi bất cứ khi nào tôi muốn lưu tệp văn bản có vẻ hơi lãng phí.

Một vấn đề khác xảy ra với phiên bản: điều gì xảy ra nếu bạn tuần tự hóa Nodebiểu đồ của mình vào ngày 1, sau đó vào ngày 2 bạn thêm một trường khác vào Node(chẳng hạn như một giá trị con trỏ khác hoặc giá trị nguyên thủy), sau đó vào ngày 3 bạn hủy tuần tự hóa tệp của mình từ 1 ngày?

Bạn cũng phải xem xét những thứ khác, như endianness. Một trong những lý do chính khiến các tệp MacOS và IBM / Windows / PC không tương thích với nhau trong những năm 1980 và 1990 mặc dù được tạo ra bởi cùng một chương trình (Word, Photoshop, v.v.) là vì trên các giá trị số nguyên nhiều byte x86 / PC đã được lưu theo thứ tự cuối nhỏ, nhưng thứ tự lớn về Mac - và phần mềm không được xây dựng với tính di động đa nền tảng. Ngày nay mọi thứ tốt hơn nhờ giáo dục nhà phát triển được cải thiện và thế giới điện toán ngày càng không đồng nhất của chúng ta.


2
Đổ tất cả mọi thứ vào không gian bộ nhớ quá trình cũng sẽ là khủng khiếp vì lý do bảo mật. Một đêm chương trình có trong bộ nhớ cả 1) một số dữ liệu công khai và 2) mật khẩu, khóa bí mật hoặc khóa riêng. Khi nối tiếp cái trước, người ta không muốn tiết lộ bất kỳ thông tin nào về cái sau.
chi


15

Khó khăn thực sự đã được mô tả trong chính từ này: " ization nối tiếp ".

Câu hỏi về cơ bản là: làm thế nào tôi có thể biểu diễn một đồ thị có hướng tuần hoàn liên kết phức tạp tùy ý của các đối tượng phức tạp tùy ý như một chuỗi các byte tuyến tính?

Hãy suy nghĩ về nó: một chuỗi tuyến tính giống như một đồ thị có hướng suy biến trong đó mọi đỉnh có chính xác một cạnh đến và đi (ngoại trừ "đỉnh đầu tiên" không có cạnh đến và "đỉnh cuối" không có cạnh đi) . Và một byte rõ ràng là ít phức tạp hơn một đối tượng .

Vì vậy, có vẻ hợp lý khi chúng ta chuyển từ một biểu đồ phức tạp tùy ý sang một "biểu đồ" bị hạn chế hơn nhiều (thực ra chỉ là một danh sách) và từ các đối tượng phức tạp tùy ý sang các byte đơn giản, thông tin sẽ bị mất, nếu chúng ta làm điều này một cách ngây thơ và không ' t mã hóa thông tin "không liên quan" theo một cách nào đó. Và đó chính xác là những gì serialization làm: mã hóa thông tin phức tạp thành một định dạng tuyến tính đơn giản.

Nếu bạn đã quen thuộc với YAML , bạn có thể xem xét các tính năng neobí danh cho phép bạn thể hiện ý tưởng rằng "cùng một đối tượng có thể xuất hiện ở những nơi khác nhau" trong một tuần tự.

Ví dụ: nếu bạn có biểu đồ sau:

A → B → D
↓       ↑
C ––––––+

Bạn có thể biểu thị đó là danh sách các đường dẫn tuyến tính trong YAML như thế này:

- [&A A, B, &D D]
- [*A, C, *D]

Bạn cũng có thể biểu diễn nó dưới dạng danh sách kề, hoặc ma trận kề hoặc như một cặp có phần tử đầu tiên là một tập hợp các nút và phần tử thứ hai là một tập hợp các nút, nhưng trong tất cả các biểu diễn đó, bạn cần phải có một cách giới thiệu ngược và chuyển tiếp tới các nút hiện có , tức là các con trỏ , mà bạn thường không có trong một tệp hoặc một luồng mạng. Tất cả những gì bạn có, cuối cùng, là byte.

(Điều BTW có nghĩa là chính tệp văn bản YAML ở trên cũng cần phải được "tuần tự hóa", đó là những gì mà các mã hóa ký tự và định dạng chuyển đổi Unicode khác nhau dành cho, nó không hoàn toàn "tuần tự hóa", chỉ là mã hóa, vì tệp văn bản đã là một chuỗi / danh sách tuyến tính của các điểm mã, nhưng bạn có thể thấy một số điểm tương đồng.)


13

Các câu trả lời khác đã giải quyết các biểu đồ đối tượng phức tạp, nhưng đáng để chỉ ra rằng các nguyên thủy tuần tự hóa cũng không tầm thường.

Sử dụng tên loại nguyên thủy C để cụ thể hóa, xem xét:

  1. Tôi nối tiếp a long. Một thời gian sau tôi de-serialize nó, nhưng ... trên một nền tảng khác nhau, và bây giờ longint64_thơn là int32_ttôi lưu trữ. Vì vậy, tôi cần phải rất cẩn thận về kích thước chính xác của mọi loại tôi lưu trữ hoặc lưu trữ một số siêu dữ liệu mô tả loại và kích thước của mọi trường.

    Lưu ý rằng nền tảng khác nhau này chỉ có thể là cùng một nền tảng sau khi biên dịch lại trong tương lai.

  2. Tôi nối tiếp một int32_t. Một thời gian sau tôi hủy đăng ký nó, nhưng ... trên một nền tảng khác, và bây giờ giá trị bị hỏng. Đáng buồn là tôi đã lưu giá trị trên một nền tảng lớn về cuối, và tải nó trên một nền tảng nhỏ. Bây giờ tôi cần thiết lập một quy ước cho định dạng của mình hoặc thêm nhiều siêu dữ liệu mô tả về tuổi thọ của mỗi tệp / luồng / bất cứ thứ gì. Và, tất nhiên, thực sự thực hiện các chuyển đổi thích hợp.

  3. Tôi nối tiếp một chuỗi. Lần này, một nền tảng sử dụng charvà UTF-8, và một wchar_tvà UTF-16.

Vì vậy, tôi cho rằng việc xê-ri hóa chất lượng hợp lý không tầm thường ngay cả đối với người nguyên thủy trong bộ nhớ liền kề. Có rất nhiều quyết định mã hóa mà bạn cần để tài liệu hoặc mô tả với siêu dữ liệu nội tuyến.

Biểu đồ đối tượng thêm một lớp phức tạp khác lên trên đó.


6

Có nhiều khía cạnh:

Khả năng đọc của cùng một chương trình

Chương trình của bạn đã lưu trữ dữ liệu của bạn bằng cách nào đó dưới dạng byte trong bộ nhớ. Nhưng nó có thể được phân tán tùy ý trên các thanh ghi khác nhau, với các con trỏ quay qua lại giữa các phần nhỏ hơn của nó [sửa: Như đã nhận xét, dữ liệu về mặt vật lý có nhiều khả năng trong bộ nhớ chính hơn là một thanh ghi dữ liệu, nhưng điều đó không làm mất đi vấn đề con trỏ] . Chỉ cần nghĩ về một danh sách số nguyên liên kết. Mỗi phần tử danh sách có thể được lưu trữ ở một nơi hoàn toàn khác nhau và tất cả các phần giữ danh sách cùng nhau là các con trỏ từ phần tử này sang phần tử tiếp theo. Nếu bạn đã lấy dữ liệu đó và cố gắng sao chép nó trên một máy khác chạy cùng chương trình, bạn sẽ gặp vấn đề:

  1. Đầu tiên và quan trọng nhất, thanh ghi nhấn vào dữ liệu của bạn được lưu trữ trên một máy có thể đã được sử dụng cho một thứ hoàn toàn khác trên một máy khác (ai đó đang duyệt trao đổi ngăn xếp và trình duyệt đã ăn hết bộ nhớ đó). Vì vậy, nếu bạn chỉ đơn giản ghi đè lên các thanh ghi, tạm biệt trình duyệt. Vì vậy, bạn sẽ cần sắp xếp lại các con trỏ trong cấu trúc để phù hợp với các địa chỉ bạn có miễn phí trên máy thứ hai. Vấn đề tương tự phát sinh khi bạn cố tải lại dữ liệu trên cùng một máy sau đó.
  2. Điều gì xảy ra nếu một số điểm thành phần bên ngoài vào cấu trúc của bạn hoặc cấu trúc của bạn có con trỏ tới dữ liệu ngoài, bạn không truyền tải? Segfaults ở khắp mọi nơi! Điều này sẽ trở thành một cơn ác mộng gỡ lỗi.

Khả năng đọc của chương trình khác

Giả sử bạn quản lý để chỉ phân bổ đúng địa chỉ trên một máy khác, để dữ liệu của bạn phù hợp với. Nếu dữ liệu của bạn được xử lý bởi một chương trình riêng biệt trên máy đó (ngôn ngữ khác), chương trình đó có thể có sự hiểu biết cơ bản hoàn toàn khác về dữ liệu. Giả sử bạn có các đối tượng C ++ có con trỏ, nhưng ngôn ngữ đích của bạn thậm chí không hỗ trợ con trỏ ở cấp độ đó. Một lần nữa, bạn không có cách nào để xử lý dữ liệu đó trong chương trình thứ hai. Bạn kết thúc với một số dữ liệu nhị phân trong bộ nhớ, nhưng sau đó, bạn cần phải viết thêm mã bao quanh dữ liệu và bằng cách nào đó chuyển nó thành thứ gì đó mà ngôn ngữ đích của bạn có thể làm việc. Nghe giống như khử lưu huỳnh, chỉ là điểm bắt đầu của bạn bây giờ là đối tượng lạ nằm rải rác xung quanh bộ nhớ chính của bạn, điều này khác với các ngôn ngữ nguồn khác nhau, thay vì một tập tin với cấu trúc được xác định rõ. Tất nhiên, điều tương tự, nếu bạn cố gắng diễn giải trực tiếp tệp nhị phân bao gồm các con trỏ - bạn cần viết các trình phân tích cú pháp cho mọi cách có thể mà một ngôn ngữ khác có thể biểu thị dữ liệu trong bộ nhớ.

Khả năng đọc của một con người

Hai trong số các ngôn ngữ tuần tự hóa hiện đại nổi bật nhất cho tuần tự hóa dựa trên web (xml, json) có thể dễ dàng hiểu được bởi một con người. Thay vì một đống nhị phân, cấu trúc và nội dung thực tế của dữ liệu rõ ràng ngay cả khi không có chương trình đọc dữ liệu. Điều này có nhiều lợi thế:

  • gỡ lỗi dễ dàng hơn -> nếu có sự cố trong đường ống dịch vụ của bạn, bạn chỉ cần xem dữ liệu xuất phát từ một dịch vụ và kiểm tra xem nó có hợp lý không (như bước đầu tiên); bạn cũng trực tiếp xem liệu dữ liệu có giống như bạn nghĩ không, khi bạn viết giao diện xuất của bạn ở vị trí đầu tiên.
  • khả năng lưu trữ: nếu bạn có dữ liệu của mình như một đống nhị phân thuần túy và bạn mất chương trình có nghĩa là diễn giải nó, bạn sẽ mất dữ liệu (hoặc bạn sẽ phải mất khá nhiều thời gian để thực sự tìm thấy thứ gì đó trong đó); nếu dữ liệu tuần tự của bạn có thể đọc được bằng con người, bạn có thể dễ dàng sử dụng nó làm kho lưu trữ hoặc lập trình trình nhập khẩu của riêng bạn cho một chương trình mới
  • bản chất khai báo của dữ liệu được tuần tự hóa theo cách như vậy, cũng có nghĩa là, nó hoàn toàn độc lập với hệ thống máy tính và phần cứng của nó; bạn có thể tải nó vào một máy tính lượng tử được xây dựng hoàn toàn khác hoặc lây nhiễm AI ngoài hành tinh bằng các sự kiện thay thế để nó vô tình bay vào mặt trời tiếp theo (Emmerich nếu bạn đọc điều này, một tài liệu tham khảo sẽ rất hay, nếu bạn sử dụng ý tưởng đó cho ngày 4 tháng 7 tới bộ phim)

Dữ liệu của tôi có lẽ chủ yếu là trong bộ nhớ chính, không phải trong sổ đăng ký. Nếu dữ liệu của tôi phù hợp với các thanh ghi, việc tuần tự hóa hầu như không thành vấn đề. Tôi nghĩ bạn đã hiểu nhầm đăng ký là gì.
David Richerby

Thật vậy, tôi đã sử dụng thuật ngữ đăng ký quá lỏng lẻo ở đây. Nhưng điểm chính là dữ liệu của bạn có thể chứa con trỏ đến không gian địa chỉ để xác định các thành phần của chính nó hoặc để tham chiếu đến dữ liệu khác. Không quan trọng nếu đó là một thanh ghi vật lý hoặc một địa chỉ ảo trong bộ nhớ chính.
Frank Hopkins

Không, bạn đã sử dụng thuật ngữ "đăng ký" hoàn toàn không chính xác. Những thứ bạn đang gọi các thanh ghi nằm trong một phần hoàn toàn khác của hệ thống phân cấp bộ nhớ với các thanh ghi thực tế.
David Richerby

6

Ngoài những gì các câu trả lời khác đã nói:

Đôi khi bạn muốn tuần tự hóa những thứ không phải là dữ liệu thuần túy.

Ví dụ, nghĩ về một tệp xử lý hoặc kết nối đến máy chủ. Mặc dù xử lý tập tin hoặc ổ cắm là một int, con số này là vô nghĩa vào lần tiếp theo chương trình chạy. Để tạo lại chính xác các đối tượng có chứa các thẻ điều khiển cho những thứ đó, bạn cần mở lại các tệp và tạo lại các kết nối và quyết định phải làm gì nếu điều này không thành công.

Nhiều ngôn ngữ ngày nay hỗ trợ lưu trữ các hàm ẩn danh trong các đối tượng, ví dụ như một onBlah()trình xử lý trong Javascript. Đây là một thách thức vì mã như vậy có thể chứa các tham chiếu đến các phần dữ liệu bổ sung mà lần lượt cần phải được tuần tự hóa. (Và sau đó, có vấn đề về mã tuần tự theo cách đa nền tảng, rõ ràng là dễ dàng hơn cho các ngôn ngữ được giải thích.) Tuy nhiên, ngay cả khi chỉ một tập hợp con của ngôn ngữ có thể được hỗ trợ, nó vẫn có thể tỏ ra khá hữu ích. Không có nhiều cơ chế tuần tự hóa cố gắng tuần tự hóa mã, nhưng xem serialization-javascript .

Trong trường hợp bạn muốn tuần tự hóa một đối tượng nhưng nó chứa thứ gì đó không được cơ chế tuần tự hóa của bạn hỗ trợ, bạn cần viết lại mã theo cách làm việc xung quanh vấn đề này. Chẳng hạn, bạn có thể sử dụng enum thay cho các hàm ẩn danh khi có một số hữu hạn các hàm có thể.

Thường thì bạn muốn dữ liệu nối tiếp là ngắn gọn.

Nếu bạn đang gửi dữ liệu qua mạng hoặc thậm chí lưu trữ trên đĩa, điều quan trọng là phải giữ kích thước nhỏ. Một trong những cách dễ nhất để đạt được điều này là loại bỏ thông tin có thể được xây dựng lại (ví dụ: loại bỏ bộ đệm, bảng băm và biểu diễn thay thế của cùng một dữ liệu).

Tất nhiên, lập trình viên phải chọn thủ công những gì sẽ được lưu và những gì sẽ được loại bỏ và đảm bảo mọi thứ được xây dựng lại khi đối tượng được tạo lại.

Hãy nghĩ về hành động cứu một trò chơi. Các đối tượng có thể chứa nhiều con trỏ tới dữ liệu đồ họa, dữ liệu âm thanh và các đối tượng khác. Nhưng hầu hết những thứ này có thể được tải từ các tệp dữ liệu trò chơi và không cần phải được lưu trữ trong một tệp lưu. Việc loại bỏ nó có thể rất tốn công vì vậy những thứ nhỏ thường bị bỏ lại. Tôi đã chỉnh sửa một số tệp lưu trong thời gian của mình và phát hiện ra dữ liệu rõ ràng là dư thừa, như mô tả mục văn bản.

Đôi khi, không gian không quan trọng nhưng khả năng đọc là trong trường hợp bạn có thể sử dụng định dạng ASCII (có thể là JSON hoặc XML).


3

Hãy xác định chuỗi byte thực sự là gì. Một chuỗi các byte bao gồm một số nguyên không âm được gọi là độ dài và một số hàm / tương ứng tùy ý ánh xạ bất kỳ số nguyên i nào có ít nhất bằng 0 và nhỏ hơn độ dài thành một giá trị byte (một số nguyên từ 0 đến 255).

Nhiều đối tượng bạn xử lý trong một chương trình thông thường không ở dạng đó, vì các đối tượng thực sự được tạo thành từ nhiều cấp phát bộ nhớ khác nhau ở các vị trí khác nhau trong RAM và có thể được phân tách khỏi nhau bởi hàng triệu byte thứ bạn đừng quan tâm. Chỉ cần nghĩ về một danh sách được liên kết cơ bản: mỗi nút trong danh sách là một chuỗi byte, vâng, nhưng các nút nằm ở nhiều vị trí khác nhau trong bộ nhớ máy tính của bạn và chúng được kết nối với các con trỏ. Hoặc chỉ cần nghĩ về một cấu trúc đơn giản có một con trỏ đến một chuỗi có độ dài thay đổi.

Lý do tại sao chúng tôi muốn tuần tự hóa các cấu trúc dữ liệu thành một chuỗi byte thường là vì chúng tôi muốn lưu trữ chúng trên đĩa hoặc gửi chúng đến một hệ thống khác (ví dụ qua mạng). Nếu bạn cố lưu trữ một con trỏ trên đĩa hoặc gửi nó đến một hệ thống khác, nó sẽ khá vô dụng vì chương trình đọc con trỏ đó sẽ có sẵn một bộ vùng nhớ khác.


1
Tôi không chắc đó là một định nghĩa tuyệt vời của một chuỗi. Hầu hết mọi người sẽ định nghĩa một chuỗi là, tốt, một chuỗi: một chuỗi các thứ nối tiếp nhau. Theo định nghĩa của bạn, int seq(int i) { if (0 <= i < length) return i+1; else return -1;}là một chuỗi. Vì vậy, làm thế nào tôi sẽ lưu trữ nó trên đĩa?
David Richerby

1
Nếu độ dài là 4, tôi lưu trữ tệp bốn byte với nội dung: 1, 2, 3, 4.
David Grayson

1
@DavidR Richby Định nghĩa của anh ấy tương đương với "một dòng những thứ khác", nó chỉ là một định nghĩa toán học và chính xác hơn so với định nghĩa trực quan của bạn. Lưu ý rằng hàm của bạn không phải là một chuỗi vì để có một chuỗi bạn cần hàm đó một số nguyên khác được gọi là độ dài.
dùng253751

1
@FreshAir Quan điểm của tôi là chuỗi là 1, 2, 3, 4, 5. Điều tôi viết ra là một hàm . Một chức năng không phải là một chuỗi.
David Richerby

1
Một cách đơn giản để ghi một hàm vào đĩa là cách tôi đã đề xuất: đối với mọi đầu vào có thể, hãy lưu trữ đầu ra. Tôi nghĩ có lẽ bạn vẫn không hiểu nhưng tôi không biết phải nói gì. Bạn có biết rằng trong các hệ thống nhúng, người ta thường chuyển đổi các hàm đắt tiền như sinthành bảng tra cứu, đó là một chuỗi các số? Bạn có biết chức năng của bạn giống như chức năng này cho các đầu vào mà chúng ta quan tâm không? int seq(n) { int a[] = [1, 2, 3, 4]; return a[n]; } Tại sao chính xác bạn nói rằng tệp bốn byte của tôi là một đại diện không đầy đủ?
David Grayson

2

Sự phức tạp phản ánh sự phức tạp của dữ liệu và đối tượng. Những đối tượng này có thể là đối tượng trong thế giới thực, hoặc chỉ đối tượng máy tính. Câu trả lời là trong tên. Tuần tự hóa là biểu diễn tuyến tính của các đối tượng đa chiều. Có nhiều vấn đề khác ngoài RAM bị phân mảnh.

Nếu bạn có thể làm phẳng 12 mảng năm chiều và một số mã chương trình, tuần tự hóa cũng cho phép bạn chuyển toàn bộ chương trình máy tính (và dữ liệu) giữa các máy. Các giao thức điện toán phân tán như RMI / CORBA sử dụng rộng rãi tuần tự hóa để truyền dữ liệu và chương trình.

Hãy xem xét hóa đơn điện thoại của bạn. Nó có thể là một đối tượng duy nhất, bao gồm tất cả các cuộc gọi của bạn (danh sách các chuỗi), số tiền phải trả (số nguyên) và quốc gia. Hoặc hóa đơn điện thoại của bạn có thể được đưa ra từ bên trên và bao gồm các cuộc gọi điện thoại được chia thành từng mục riêng biệt được liên kết với tên của bạn. Mỗi lần làm phẳng sẽ trông khác nhau, phản ánh cách công ty điện thoại của bạn viết phiên bản phần mềm đó và lý do cơ sở dữ liệu hướng đối tượng không bao giờ tắt.

Một số phần của cấu trúc thậm chí có thể không có trong bộ nhớ. Nếu bạn có bộ nhớ đệm lười biếng, một số phần của một đối tượng chỉ có thể được tham chiếu đến một tệp đĩa và chỉ được tải khi phần đó của đối tượng cụ thể đó được truy cập. Điều này là phổ biến trong khuôn khổ kiên trì nghiêm trọng. BLOB là một ví dụ tốt. Getty Images có thể lưu trữ một bức tranh khổng lồ nhiều megabyte của Fidel Castro và một số dữ liệu meta như tên của hình ảnh, chi phí thuê và chính hình ảnh. Bạn có thể không muốn tải hình ảnh 200 MB vào bộ nhớ mỗi lần, trừ khi bạn thực sự nhìn vào anh ta. Được nối tiếp, toàn bộ tệp sẽ yêu cầu hơn 200 MB dung lượng lưu trữ.

Một số đối tượng thậm chí không thể được nối tiếp cả. Trong vùng đất lập trình Java, bạn có thể có một đối tượng lập trình đại diện cho màn hình đồ họa hoặc một cổng nối tiếp vật lý. Không có khái niệm thực sự về việc xâu chuỗi một trong hai. Làm thế nào bạn sẽ gửi cổng của bạn cho người khác qua mạng?

Một số thứ như mật khẩu / khóa mã hóa không nên được lưu trữ hoặc truyền đi. Chúng có thể được gắn thẻ như vậy (dễ bay hơi / thoáng qua, v.v.) và quá trình tuần tự hóa sẽ bỏ qua chúng nhưng chúng có thể sống trong RAM. Bỏ qua các thẻ này là cách các khóa mã hóa vô tình được gửi / lưu trữ trong ASCII đơn giản.

Câu trả lời này và các câu trả lời khác là tại sao nó phức tạp.


2

Vấn đề tôi gặp phải là: không phải tất cả các biến (có thể là nguyên thủy như các đối tượng int hoặc composite) đã được biểu diễn bằng một chuỗi byte?

Vâng, họ là. Vấn đề ở đây là cách bố trí các byte đó. Một đơn giản intcó thể dài 2, 4 hoặc 8 bit. Nó có thể ở endian lớn hoặc nhỏ. Nó có thể không được ký, ký với phần bổ sung 1 hoặc thậm chí trong một số mã hóa siêu kỳ lạ như negabinary.

Nếu bạn chỉ bỏ rác một cách intngẫu nhiên từ bộ nhớ và gọi nó là "tuần tự hóa", bạn phải đính kèm khá nhiều toàn bộ máy tính, hệ điều hành và chương trình của bạn để nó có thể khử được. Hoặc ít nhất, một mô tả chính xác về họ.

Vì vậy, những gì làm cho serialization như một chủ đề sâu sắc? Để tuần tự hóa một biến, chúng ta có thể lấy các byte này trong bộ nhớ và ghi chúng vào một tệp không? Những gì phức tạp tôi đã bỏ lỡ?

Việc tuần tự hóa một đối tượng đơn giản là viết ra nó theo một số quy tắc. Những quy tắc đó rất nhiều và không phải lúc nào cũng rõ ràng. Ví dụ: một xs:integerXML được viết trong cơ sở 10. Không phải cơ sở-16, không phải cơ sở-9, nhưng 10. Đây không phải là một giả định ẩn, đó là một quy tắc thực tế. Và các quy tắc như vậy làm cho tuần tự hóa một tuần tự. Bởi vì, khá nhiều, không có quy tắc nào về cách bố trí bit của chương trình của bạn trong bộ nhớ .

Đó chỉ là một đỉnh của tảng băng trôi. Hãy lấy một ví dụ về một chuỗi những nguyên thủy đơn giản nhất: a C struct. Bạn có thể nghĩ rằng

struct {
short width;
short height;
long count;
}

có bố cục bộ nhớ xác định trên một máy tính nhất định + HĐH không? Vâng, nó không. Tùy thuộc vào #pragma packcài đặt hiện tại , trình biên dịch sẽ đệm các trường. Trên cài đặt mặc định của trình biên dịch 32 bit, cả hai shortssẽ được đệm thành 4 byte để structthực sự sẽ có 3 trường 4 byte trong bộ nhớ. Vì vậy, bây giờ, bạn không chỉ phải xác định shortdài 16 bit, đó là một số nguyên, được viết bằng 1 bổ sung âm, cuối hoặc lớn. Bạn cũng phải ghi lại cài đặt đóng gói cấu trúc mà chương trình của bạn được biên dịch.

Đó là khá nhiều những gì nối tiếp là về: tạo ra một bộ quy tắc và tuân theo chúng.

Các quy tắc đó sau đó có thể được mở rộng để chấp nhận các cấu trúc phức tạp hơn (như danh sách độ dài thay đổi hoặc dữ liệu phi tuyến), các tính năng được thêm vào như khả năng đọc của con người, phiên bản, khả năng tương thích ngược và sửa lỗi, v.v. Nhưng ngay cả việc viết ra một đơn intcũng đủ phức tạp nếu bạn chỉ muốn chắc chắn rằng bạn sẽ có thể đọc lại một cách đáng tin cậy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.