C / دليل حلول تحريري

إصلاح عدم تطابق الإدخال والأسطر الجديدة المتبقية

افصل جلب السطر الكامل عن التحليل العددي حتى لا تغير التحويلات الفاشلة والفواصل المتبقية السجل التالي بصمت.

وقت القراءة التقريبي: 3 دقيقةتاريخ مراجعة المحتوى

قاعدة معرفة للقراءة فقط · بلا تعليقات عامة

تعرّف على الأعراض

بعد scanf عددية تبدو fgets التالية وكأنها تقرأ اسما فارغا. وقد يجعل النص غير العددي حلقة الإعادة تتكرر بلا نهاية، أو تُقبل بادئة مثل 12x دون توقع. تشير هذه السلوكيات إلى استهلاك الدفق وسياسة التحقق، لا إلى غياب تأخير بين القراءات.

افهم الأسباب المحتملة

  • يترك تحويل %d الناجح عادة السطر الجديد اللاحق دون قراءة. وقد يترك فشل المطابقة المحارف المسببة في الدفق. يسمح تجاهل عدد تحويلات scanf عندها بقيم قديمة أو فشل متكرر. تحويل النص غير المحدود غير آمن، وليست fflush(stdin) حلا محمولا في C17 لمسح الإدخال.

1. تتبع المحارف المستهلكة بدقة

اكتب المدخل الاصطناعي كسجلات ظاهرة، مثل سطر عدد ثم سطر اسم. حدد المحارف التي تستهلكها كل قراءة وما يبقى. لا تختلق fgets سطرا فارغا؛ فقد تعيد بالفعل نهاية السطر التي تركها التحويل العددي. ميز هذه الحالة من نهاية الملف بفحص إرجاعها قبل فحص المخزن.

2. اختيار سياسة سجل محدودة واحدة

في هذا الإصلاح يمثل كل سطر فعلي سجلا ويجب أن يلائم مخزنه مع نهاية السطر والنهاية الصفرية، وأن يحتوي السطر العددي أرقاما عشرية دون لاحقة. تُرفض السجلات الأطول بدلا من تقسيمها. قد يقبل قارئ إنتاجي السطر الأخير دون سطر جديد، لكن ذلك يحتاج سياسة نهاية ملف صريحة؛ ويشترط المثال المضبوط نهاية السطر.

3. الجلب أولا ثم التحويل والتحقق

يستخدم المثال الكامل دفقا مؤقتا بسجلات مختبر معروفة. يفحص القراءة المحدودة ونهايات السطور ثم يحلل بـstrtol ويفحص غياب الأرقام واللاحقة وERANGE والمجال، وبعدها يقرأ الاسم مستقلا. يُعامل الاسم كبيانات عبر %s. لا يلزم تحويل غير محدود ولا تفريغ إدخال خاص بالمنصة.

c قراءة سجلين مستقلين وتحليلهما
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char number[16], name[16];
    FILE *input = tmpfile();
    if (input == NULL) return 1;
    if (fputs("12\nAda\n", input) == EOF) { fclose(input); return 1; }
    rewind(input);
    if (fgets(number, sizeof number, input) == NULL) { fclose(input); return 1; }
    char *newline = strchr(number, '\n');
    if (newline == NULL) { fclose(input); return 1; }
    *newline = '\0';
    char *end;
    errno = 0;
    long value = strtol(number, &end, 10);
    if (end == number || *end != '\0' || errno == ERANGE || value < 0 || value > 100) {
        fclose(input); return 1;
    }
    if (fgets(name, sizeof name, input) == NULL) { fclose(input); return 1; }
    newline = strchr(name, '\n');
    if (newline == NULL) { fclose(input); return 1; }
    *newline = '\0';
    printf("count=%ld name=%s\n", value, name);
    return fclose(input) == 0 ? 0 : 1;
}
الناتج المتوقع
count=12 name=Ada

تستهلك fgets الأولى سطر العدد كاملا فتجلب القراءة التالية Ada.

4. فحص scanf والتعافي المقصود عند الإبقاء عليها

إذا أبقت الواجهة scanf فاشترط عدد الإسنادات المتوقع وحدد استهلاك بقية السجل الحالي بعد النجاح أو فشل المطابقة. لا تكفي getchar واحدة عند وجود فراغات لاحقة أو نص زائد أطول. استخدم حلقة مضبوطة حتى نهاية السطر أو EOF وميز خطأ الإدخال. لا تضف توجيه فراغ لاحقا عشوائيا؛ فقد ينتظر محرفا غير فراغي لاحقا.

5. فصل الصياغة عن مدى التطبيق

قد تكون صياغة السجل عددية لكنه خارج مجال التطبيق. افحص مدى التمثيل قبل التضييق من long إلى int ثم طبق حدودا مثل صفر حتى مئة. لا يحدث المستدعي العدد المخزن إلا بعد نجاح كل فحص. عند الرفض احفظ الحالة الصالحة السابقة وأبلغ السجل المطلوب تصحيحه بدلا من متابعة بادئة محللة جزئيا.

تحقق من نجاح الإصلاح

يجب أن يطبع برنامج المختبر count=12 name=Ada. اختبر سطرا غير عددي و12x و101 وسطرا فارغا ونهاية الإدخال وطولا عند السعة أو بعدها؛ ويجب ألا تُقبل بيانات قديمة في أي منها. تحقق من بدء السجل التالي عند حده المقصود بعد كل قبول أو رفض وفق سياسة التعافي المختارة.

امنع تكرار المشكلة

  • فضل قارئ أسطر محدودا واحدا ومحللا مستقلا لكل نوع سجل. وثق معالجة نهايات السطر والبتر وافحص إرجاع المكتبة دائما واختبر أطوال الحدود. أبق النص الخارجي خارج نصوص التنسيق واستخدم %ld لقيم long والمحددات المطابقة للأنواع الفعلية.

المراجع الرسمية