سؤال

لدي ملف نصي مع ما يصل إلى 100 عنوان IP، 1 لكل سطر. أحتاج إلى قراءة كل عنوان، كسلسلة، في صفيف يسمى "قائمة". أولا، أفترض أن "القائمة" ستحتاج إلى أن تكون صفيفا سحر ثنائي الأبعاد. طول عنوان IP 11 حرفا في الطول، 12 إذا قمت بتضمين " 0"، لذلك أعلنت قائمة على النحو التالي:

char list[100][12];

بعد ذلك، أحاول استخدام fgets لقراءة الدفق:

  for (i = 0; i < 100; i++)  
  {  
      if (feof(stream))  
          break;  
          for (j = 0; j < 12; j++)  
          fgets(&list[i][j], 12, stream);  
      count++;  
  }

للتحقق لمعرفة ما إذا كانت السلاسل تم قراءتها بشكل صحيح، أحاول إخراجها:

  for (i = 0; i < 5; i++)  
  {  
      for (j = 0; j < 11; j++)  
          printf("%c", list[i][j]);  
      printf("\n");  
  }

بعد تشغيل البرنامج، من الواضح أن هناك خطأ ما. كونك مبتدئا، لست متأكدا ما، لكنني أظن أنني أقرأ الملف خطأ. لا توجد أخطاء. يجمع، ولكن يطبع عنوانا غريبا على سطرين.

يحرر:

لقد استبدلت رمز FGETS مع هذا:

for (i = 0; i < 100; i++)
  {
      if (feof(stream))
          break;
      fgets(list[i], 12, stream);
      count++;
  }

يطبع الآن خمس سلاسل، لكنهم شخصيات "عشوائية" من الذاكرة.

هل كانت مفيدة؟

المحلول

أولا، القراءة:

      for (j = 0; j < 12; j++)  
      fgets(&list[i][j], 12, stream);  

لديك مشكلة كبيرة هنا. هذا يحاول قراءة سلسلة في كل متتالية حرف في صفيفك.

الكل في الكل، أعتقد أنك تجعل هذا أكثر تعقيدا مما يجب أن يكون. فكر في صفيفك باعتبارها 100 سلاسل، و fgets سوف تعمل مع سلسلة في وقت واحد. وهذا يعني أن القراءة يمكن أن تبدو مثل هذا:

for (i=0; i<100 && fgets(list[i], 11, string); i++)
    ;

هناك تفاصيل ثانوية أخرى للتعامل معها: fgets() يحتفظ عادة الخط الجديد في نهاية كل سطر. على هذا النحو، قد تحتاج إلى مغادرة المجال لمدة 13 حرفا (11 مقابل العنوان، 1 للخط الجديد، 1 من أجل NUL Terminator)، وإلا فقد ترغب في قراءة البيانات في مخزن مؤقت مؤقت، ونسخها إلى list فقط بعد أن جردت قبالة الخط الجديد.

في التعليمات البرمجية الحالية لطباعة الأوتار، كنت تعمل حرفا واحدا في كل مرة، والتي يمكن أن تعمل، ولكنها صعبة بشكل غير ضروري. اقترح العديد من الأشخاص استخدام تحويل Printf٪ S، وهو ما يرام في حد ذاته. للذهاب معها، ومع ذلك، عليك تبسيط الفهرسة قليلا. طباعة أول ستة عناوين ستبدو شيئا مثل هذا:

for (i=0; i<6; i++)
    printf("%s", list[i]);

نصائح أخرى

مكالمتك إلى fgets يقرأ ما يصل إلى 11 حرفا من الدفق إلى الصفيف. لذلك لا تريد أن تدعو ذلك مرة واحدة لكل حرف من كل سلسلة.

مجرد التفكير في تلك الحلقات: مع I = 0 و J = 0، فإنه يقرأ ما يصل إلى 11 حرفا &list[0][0]. وبعد ثم مع i = 0 و J = 1، يقرأ 11 حرفا آخر &list[0][1]. وبعد هذا خطأ لسببين - إنه يبحث عن نتيجة للمكالمات الأخيرة، وربما يكتب المزيد من بايت من القائمة [0] يمكن أن تعقد.

يحقق حرف Newline fgets عن القراءة، ولكنه يعتبر حرفا صالحا، وبالتالي يتم تضمينه في السلسلة التي تم نسخها إلى Str.

قد تقرأ أول 12 حرفا في أول استدعاء FGENTS، ثم الدعوة الثانية ستقبض على الخط الجديد، ثم تحصل المكالمة الثالثة على السطر التالي.

حاول استخدام FENTS مع حد 15 حرفا، وتوسيع المخزن المؤقت الخاص بك.

الحلقة الثانية ليست ضرورية وتفسد ذاكرتك. يجب أن تفعل شيئا مثل هذا،

for (i = 0; i < 100; i++)
{
if (feof(stream))
break;
fgets(&list[i][j], 12, stream);
count++;
}

To check to see if the strings were read properly, I attempt to output them:

for (i = 0; i < 5; i++)
{
printf("%s\n", list[i]);
}

ل (i = 0؛ أنا <100؛ i ++) {

   if (feof(fp))
       break;

   fscanf(fp,"%s\n",list[i]);

}

لا تستخدم feof() كما شرط حلقة الخاص بك؛ لن يعود هذا صحيحا حتى لو كنت قد حاول أن تقرأ بعد نهاية الملف، مما يعني أن حلقة الخاص بك ستنفذ مرة واحدة كثيرة جدا. تحقق من نتيجة مكالمة المدخلات الخاصة بك (سواء كنت تستخدم fgets() أو fscanf()) لمعرفة ما إذا كانت قد نجحت، ومن بعد التحقق من feof() إذا حصلت على حالة خطأ.

if (fgets(buffer, sizeof buffer, stream) != NULL)
{
  // process the input buffer
}
else if (feof(stream)
{
  // handle end of file
}
else
{
  // handle read error other than EOF
}

fgets() يقرأ السلاسل بأكملها، وليس الأحرف الفردية، لذلك لا تريد تمرير عنوان كل حرف فردي في السلسلة الخاصة بك. نسميها كما لو بدلا من ذلك:

if (fgets(list[i], sizeof list[i], stream) != NULL)
{
  // process input address
}

والآن، ل Bode's Spell المعتاد حول المصفوفات والمؤشرات ...

عندما يظهر تعبير صفيف في معظم السياقات، يتم تحويل نوع التعبير ضمنا من "مجموعة عنصر N من T" إلى "المؤشر إلى T"، وقيمة التعبير هو عنوان العنصر الأول من الصفيف. الاستثناءات لهذه القاعدة هي عندما يكون تعبير الصفيف هو المعامل sizeof أو & المشغلون، أو إنها سلسلة حرفية يتم استخدامها كعمليات تهيئة في إعلان. عندما تسمع الناس يقولون "المصفوفات والمؤشرات هي نفس الشيء"، فهي تصيب هذه القاعدة. المصفوفات والمؤشرات حيوانات مختلفة تماما، ولكن يمكن استخدامها بالتبادل في بعض السياقات.

لاحظ أنه في الكود أعلاه مررت list[i] كحجة الأولى تصل إلى () دون أي زخرفة (مثل & المشغل أو العامل). على الرغم من نوع list[i] هو "صفيف عنصر 12 عنصر"، في هذا السياق يتم تحويله ضمنيا إلى نوع "المؤشر إلى Char"، وسوف تكون القيمة عنوان list[i][0]. وبعد لاحظ أنني أجرت أيضا نفس التعبير إلى sizeof المشغل أو العامل. في هذه الحالة، نوع تعبير الصفيف هو ليس تم تحويله إلى نوع المؤشر، وعامل Sizeof يعرض عدد البايتات في نوع الصفيف (12).

فقط لتسجيل ذلك أسفل:

نوع التعبير تم تحويله ضمنيا إلى ------------ ---- قائمة سحر [100] [12] char (*) [12] (مؤشر إلى 12 عنصر من العناصر من char) i] char [12] char * قائمة [i] [j] char n / a

ما يعنيه كل هذا هو ذلك fgets() سيتم قراءة ما يصل إلى الأحرف ال 12 التالية (شريطة أنه لا يصل إلى خط جديد أو EOF أولا) وتخزينه يبدأ في list[i][0]. وبعد لاحظ أن fgets() سوف تكتب حرف NUL إنهاء (0) حتى نهاية السلسلة الخاصة بك. لاحظ أيضا أنه إذا fgets() يواجه Newline. و هناك مجال في الصفيف المستهدف لذلك والتنهير NUL، fgets() سوف تخزن عنوان نيو لاين لذلك إذا كان ملف الإدخال الخاص بك لديه خط مثل

1.1.1.1\n

ثم محتويات المخزن المؤقت الإدخال الخاص بك بعد القراءة ستكون "1.1.1.1\n\0xxx" أين x هي بعض القيمة العشوائية. إذا كنت لا تريد الخط الجديد هناك، يمكنك استخدام strchr() وظيفة للعثور عليها ثم الكتابة فوقها مع 0:

char *newline;
...
if ((newline = strchr(input[i], '\n')) != NULL)
{
  *newline = 0;
}

حيث fgets() توقف عند Newline Newline التالي، وبما أن المخزن المؤقت للمدخلات الخاص بك هو حجم 12 حرفا، فمن الممكن لك أن تعمل في موقف حيث يكون لديك خط جديد كحرف الإدخال التالي في الملف؛ في هذه الحالة، fgets() ستكتب فقط أن NewLine إلى المخزن المؤقت للإدخال، لذلك سيكون لديك بعض الإدخالات الفارغة، والتي ربما لا تكون ما تريد. قد ترغب في إضافة بايت إضافية إلى المخزن المؤقت الإدخال الخاص بك من أجل تجنب هذا الموقف.

ضع كل شيء معا:

char list[100][13];
...
for (i = 0; i < 100; ++)
{
  if (fgets(list[i], sizeof list[i], stream) != NULL)
  {
    char *newline = strchr(list[i], '\n');
    if (newline != NULL)
      *newline = 0;
    printf("Read address \"%s\"\n", list[i]);
    count++;
  }
  else if (feof(stream))
  {
    printf("Reached end of file\n");
    break;
  }
  else
  {
    printf("Read error on input; aborting read loop\n");
    break;
  }
}

كتبت وظيفة لخطوط القراءة. أعتقد أنه ينبغي أن يكون آمنا.

تحقق: io_readline.

https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top