# حفظ البيانات واسترجاعها

**سَلْسَلة البيانات** (Data Serialization) تشير لعملية تحويل البيانات في ذاكرة بايثون (مثل القائمة `[]` والقاموس `{}`) من صيغتها الثنائية الخاص باللغة إلى تمثيلٍ ليس خاصًّا بلغةٍ معيَّنة؛ بل يتبع صيغةً متفقًا عليها؛ فإما أن يكون:

1. **تمثيلاً نصيًّا** (Plain Text Serialization) متفقًا عليه مثل: `xml` أو `json` أو `csv` ونحوها (وقد تقدَّم عرضُها في [باب النص]()).
2. أو **تمثيلاً ثنائيًّا** (Binary Serialization) متفقًا عليه كذلك مثل: `pickle` أو `protobuf` أو `parquet`.

فالتمثيل النصي يتميز بأنه مباشر وواضح بالنسبة للبشر. لكنه أبطأ في المعالجة (سواءً في الكتابة أو في القراءة) وأكبرُ حجمًا في التخزين غالبًا. أما التمثيل الثانئي فهو بعكسه: أقرب للآلة وأصعب في التدقيق عند حدوث الخطأ إلا مع وجود أدوات تساعد في ذلك. لكنه أسرع في المعالجة وأقل حجمًا للتخزين.

وعملية تحويل البيانات المُسَلسَلة (أي المكتوبة بإحدى هذه الصيغ) وقراءَتها في برنامجٍ ما تسمى فك التسلسل (Deserialization). فالتحويل يكون مثلاً  من النصي إلى الثنائي في الذاكرة. أو من الثنائي المسلسل إلى الثنائي في الذاكرة.

فالسلسلة وفكها **ما هي إلا صياغة** لا تغير المكنون بل شكله.

ويختلف شكل البيانات من ثلاثة أوجه:

1. **الصف**: `[v1, v2, ...]`
2. **الربط**: `{k1: v1, k2: v2, ...}`
3. **التضمين**: وهو جعل العنصر نفسه صفًّا أو ارتباطًا
    - تضمين الصف لصفوف: `[[v1, v2, ...], [v1, v2, ...], ...]`
    - تضمين الصف لروابط: `[{k1: v1, k2: v2, ...}, {k1: v1, k2: v2, ...}, ...]`
    - تضمين الروابط لصفوف: `{k1: [v1, v2, ...], k2: [v1, v2, ...], ...}`
    - تضمين الروابط لروابط: `{k1: {k2: v2, k3: v3, ...}, k2: {k4: v4, k5: v5, ...}, ...}`

فالصفوف تتميز بكون كل صفٍّ فيها تدوينًا لمجموعة خصائص تشترك في العمود كله. أما الارتباط ففيه مرونة؛ فمجموعة الارتباطات لا تُلزِم كل ارتباط أن يكون له ذات خصائص الارتباط الذي قبله.

## حزمة (`csv`)

```{python}
import csv
```

تسهل حزمة [`csv`]((https://docs.python.org/3/library/csv.html)) المبنية في بايثون التعامل مع البيانات المجدولة المصفصفة.

مثل الملف `abc.csv`: حيث تفصل الصفوف **بسطر** `\n` وتفصل القيَم في السطر **بالفاصلة** `,`.
حيث يرمز الاختصار CSV لكلمة Comma Separated Values؛ بمعنى: القيَم المفصول بالفاصلة.

مثالها:

```csv
Name,Age,Grade,Done
Adam,22,90,F
Belal,23,92,F
Camal,24,91,T
Dawod,8,99,F
Emad,9,98,F
```


وكذلك ملف `abc.tsv`: حيث تفصل الصفوف **بسطر** `\n` وتفصل القَم في السطر **بالمسافة البادئة** `\t`.

مثالها:

```
Name    Age    Grade  Done
Adam    22 90  F
Belal   23    92 F
Camal   24    91 T
Dawod   8 99  F
Emad    9  98   F
```

### حفظ جدول البيانات

نفترض أن لدينا قائمة من البيانات على النحو التالي:

```{python}
rows = [
    ['Adam', 22, 90, 'F'],
    ['Belal', 23, 92, 'F'],
    ['Camal', 24, 91, 'T'],
    ['Dawod', 8, 99, 'F'],
    ['Emad', 9, 98, 'F'],
]
header = ['Name', 'Age', 'Grade', 'Done']
```

- حيث تمثل القائمة `rows` الصفوف
- وتمثل القائمة `header` اسم كل عمود

فإن الكتابة تتم بإنشاء الكاتب وتمرير الملف أو مسار الملف إليه ([`csv.writer(file)`](https://docs.python.org/3/library/csv.html#csv.writer)) على النحو التالي:

```{python}
with open('students.csv', mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(header)
    writer.writerows(rows)
```

ملاحظة، يمكنك محاولة فتح الملف مباشرة من مستكشف الملفات. حاول فتحه باستخدام Excel أو Google Sheet أو أي برنامج جداول بيانات آخر. إذا فتحته باستخدام محرر نصوص، سترى البيانات كملف CSV؛ حرفيًا قيم مفصولة بفواصل.


### قراءة جدول البيانات

الآن، دعنا نقرأه كهيكل بيانات في بايثون: كقائمة من القوائم.

```{python}
data = []
with open('students.csv') as file:
    reader = csv.reader(file)
    for row in reader:
        data.append(row)
```

وتستطيع كتابة وقراءة الصفوف أيضًا باستعمال:

- قارئ القاموس [`DictReader`](https://docs.python.org/3/library/csv.html#csv.DictReader)
- كاتب القاموس [`DictWriter`](https://docs.python.org/3/library/csv.html#csv.DictWriter)

:::{.callout-note}
غالبًا نستعمل مكتبة بانداس ([Pandas](https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html)) للتعامل مع البيانات الجدولية.
:::

## حزمة (`json`)

```{python}
import json
```

تسهل حزمة [`json`]((https://docs.python.org/3/library/json.html)) المبنية في بايثون التعامل مع البيانات المضمنة.

مثل الملف `settings.json`:

```json
{
    "theme": "dark",
    "language": "Arabic",
}
```

أو الملف `students.json`:

```json
[
    ["Adam", 22, 90, "F"],
    ["Belal", 23, 92, "F"],
    ["Camal", 24, 91, "T"],
    ["Dawod", 8, 99, "F"],
    ["Emad", 9, 98, "F"],
]
```

لنفترض أن لدينا `user_preferences` محفوظًا في القاموس التالي، ونريد حفظه في ملف json:

```{python}
user_preferences = {
    'theme': 'dark',
    'language': 'Arabic',
    'notifications': {
        'email': True,
        'sms': False,
        'push': True
    },
    'last_updated': '2021-09-01',
    'emails': ['example1@domain.com', 'example2@domain.com']
}
```


### كتابة مستند بيانات

لنكتبها في الملف نستعمال `json.dump` على النحو التالي:

```{python}
with open('user_preferences.json', mode='w') as file:
    json.dump(user_preferences, file)
```


### قراءة مستند بيانات

فإذا أردنا قراءتها نستعمل `json.load` على النحو التالي:

```{python}
with open('user_preferences.json') as file:
    data = json.load(file)
print(data)
```

وانظر:

- المكتبة المبنية [`xml`](https://docs.python.org/3/library/xml.html) لصيغة `XML`
- المكتبة الخارجية [`pyyaml`](https://pyyaml.org/wiki/PyYAMLDocumentation) لصيغة `YAML`
