Go / المتوسط: الحزم والعمل المتزامن / الدرس 9
الدفقات والملفات والسجلات المحدودة
عالج نتائج الدفق بالترتيب الصحيح وافرض الحجم وحدد ملكية الموارد صراحة.
في هذه الصفحة
أهداف الدرس
- تفسير n وerr معا في عقود Reader.
- اختيار مسح محدود أو قراءة بطول دقيق.
- تحديد الإغلاق والتحقق من السجل الكامل.
قد تعيد القراءة بيانات وخطأ معا
تعيد io.Reader عدد بايتات وخطأ. يجب معالجة n الموجب قبل الخطأ المصاحب، فقد يعيد القارئ بيانات وio.EOF في الاستدعاء نفسه. أول n بايت فقط ذات معنى جديد؛ ولا تفحص السعة غير المستخدمة أو البايتات القديمة بعد قراءة قصيرة.
ليست القراءة بالضرورة سجل تطبيق كاملا. تختلف حدود الشبكة والملف عن إطارات البروتوكول. استخدم io.ReadFull للطول الثابت أو محللا يحفظ حالة السجل الجزئي. ميز النهاية النظيفة قبل السجل من البتر داخله.
طلب البايتات الموثقة بالضبط
يقرأ المثال الأول ثلاثة بايتات من مصدر ذاكرة من أربعة. تملأ io.ReadFull الشريحة المطلوبة أو تبلغ خطأ مع عدد المنقول. يبقى البايت الأخير لعملية لاحقة. يتجنب ذلك افتراض ملء Reader.Read الواحدة للمخزن كله.
يجعل المصدر الثابت المثال قابلا للتكرار دون مسارات خارجية. لطول إطار غير موثوق افحص الحد الأعلى قبل التخصيص والقراءة. لا يسمح طول التنسيق بتخصيص ذاكرة هائلة. وأضف حدودا تراكمية لدفق من سجلات كثيرة مقبولة منفردة.
package main
import (
"fmt"
"io"
"strings"
)
func main() {
input := strings.NewReader("ABCD")
data := make([]byte, 3)
n, err := io.ReadFull(input, data)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%d %q remaining=%d\n", n, string(data), input.Len())
}
3 "ABC" remaining=1
تملأ ReadFull ثلاثة بايتات بـABC وتترك D دون قراءة.
يحتاج المسح سياسة خطأ وحجم
توفر bufio.Scanner اجتياز رموز مريحا وحد حجم قابل للضبط. اضبط المخزن قبل المسح عند قبول سجلات أكبر محدودة وافحص Scanner.Err بعد false، وإلا قد تخلط السجل الطويل بالنهاية العادية. يزيل تقسيم الأسطر الافتراضي نهاية السطر من الرمز.
يستخدم المثال الثاني أسطرا صغيرة معروفة ويفحص الخطأ النهائي. يعد سجلات منطقية، بما فيها السطر الفارغ وفق السياسة. وقد تناسب bufio.Reader أو محلل محدود خاص التنسيقات التي تحتاج حفظ الفاصل أو تعافيا مضبوطا من الطول الزائد.
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
scanner := bufio.NewScanner(strings.NewReader("red\nblue\n"))
scanner.Buffer(make([]byte, 64), 1024)
count := 0
for scanner.Scan() {
count++
fmt.Println(scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
return
}
fmt.Println("records", count)
}
red
blue
records 2
ينتج رمزان دون نهايتي السطر ثم يُفحص الاكتمال الناجح.
فاتح المورد يملك إغلاقه عادة
ينبغي لمن يفتح os.File ترتيب إغلاقه بعد النجاح. لا ينقل io.Reader المستعار مسؤولية الإغلاق تلقائيا. تناسب defer ملكية الدالة، وقد تحتاج الحلقات مساعدا لكل دورة لمنع تراكم الملفات. يؤثر الإنشاء والاقتطاع في الحالة الدائمة فاستخدم مسارات مؤقتة لا ملفات مستخدم عشوائية.
افحص أخطاء الكتابة والإغلاق المناسبة عندما يهم التسليم. لا تعني الكتابة الناجحة لمخزن مؤقت حفظا دائما. الإغلاق والديمومة أمام الانهيار مختلفان بمتطلبات منصية. أبق بروتوكولات الاستبدال خارج عقد التحليل العام.
حد البايتات طبقة تحقق واحدة
تتوقف io.LimitReader عند الحد ولا تخبر تلقائيا هل بقيت بيانات. قد يكشف قراءة الحد زائد واحد المحتوى الزائد إذا اختير بأمان. افحص بعد الجلب الصياغة والمدى والمجال والسجلات اللاحقة مستقلة. وقد تحتوي القراءة المحدودة بيانات غير صالحة.
ينبغي للمحللات إعادة أخطاء ذات سياق دون تسجيل الفشل في كل طبقة. اختبر الفراغ والحد الدقيق وبايتا زائدا وإطارا مبتورا وقارئا يعيد بيانات وEOF معا. تعتمد النتيجة الصحيحة على سياسة السجل لا مجرد وصول بايتات.
الأخطاء الشائعة وإصلاحها
تختفي البايتات الأخيرة عند EOF.
- لماذا يحدث؟
- تعالج الحلقة err قبل n.
- كيف تصلحه؟
- استهلك أول n بايت ثم صنف الخطأ.
- كيف تتجنب تكراره؟
- اختبر Reader يعيد بيانات وEOF معا.
يبدو السطر الطويل اكتمالا نظيفا.
- لماذا يحدث؟
- أُهملت Scanner.Err.
- كيف تصلحه؟
- افحص الخطأ النهائي واضبط حد الرمز.
- كيف تتجنب تكراره؟
- اختبر الحد وبايتا فوقه.
يغلق محلل دفقا يحتاجه المستدعي.
- لماذا يحدث؟
- لم تُحدد الاستعارة والملكية.
- كيف تصلحه؟
- اترك الدفقات المستعارة مفتوحة إلا بنقل صريح.
- كيف تتجنب تكراره؟
- وثق مكتسب كل مورد ومغلقه.
اختبر فهمك
1. تعيد قراءة إطار من أربعة n=2 وio.ErrUnexpectedEOF. ماذا يفعل المحلل بحالته؟
اعرض الحل المشروح
عامل الإطار كناقص ولا تفسر البايتات غير المملوءة. أبلغ البتر بسياقه واحفظ الحالة المعتمدة السابقة ما لم توثق الواجهة نتائج جزئية. لا تحتفظ بالبايتين إلا داخل محلل تدريجي مصمم. ويظل التنظيف لازما رغم فشل البيانات.
2. لماذا لا تثبت io.ReadAll(io.LimitReader(r, 100)) أن المصدر لا يتجاوز مئة بايت؟
اعرض الحل المشروح
تقرأ مئة كحد أقصى وقد تتوقف عند الحد الاصطناعي رغم وجود المزيد. لكشف الزائد اقرأ حتى مئة وواحد وارفض طولا فوق مئة مع معالجة الخطأ والملكية. وحد حساب الحجم ووقت العملية أيضا.
أهم ما تعلمته
- عالج البايتات المعادة قبل خطأ القراءة المصاحب.
- تحتاج حدود الرموز والسجلات سلوك رفض صريحا.
- الاستعارة والإغلاق والتحقق والديمومة عقود منفصلة.