اذهب إلى المحتوى

دوال التعامل مع السلاسل النصية والوقت والتاريخ في لغة سي C


Naser Dakhel

نتطرّق في هذا المقال إلى طرق مختلفة في التعامل مع السلاسل النصية والتلاعب بها، وذلك عن طريق دوال مكتبة string.h، ومن ثمّ ننتقل إلى دوال الوقت والتاريخ المحتواة في مكتبة time.h.

التعامل مع السلاسل النصية

هناك العديد من الدوال التي تسمح لنا بالتعامل مع السلاسل النصية، إذ تكون السلسلة النصية في لغة سي مؤلفةً من مصفوفة من المحارف تنتهي بمحرف فارغ null، وتتوقع الدوال في جميع الحالات تمرير مؤشر يشير إلى المحرف الأول ضمن السلسلة النصية، ويعرّف ملف الترويسة <string.h> هذا النوع من الدوال.

النسخ

يضم هذا التصنيف الدوال التالية:

#include <string.h>

void *memcpy(void *s1, const void *s2, size_t n);
void *memmove (void *s1, const void *s2, size_t n);
char *strcpy(char *s1, const char *s2);
char *strncpy(char *s1, const char *s2, size_t n);
char *strcat(char *s1, const char *s2);
char *strncat(char *s1, const char *s2, size_t n);
  • دالة memcpy: تنسخ هذه الدالة n بايت من المكان الذي يشير إليه المؤشر s2 إلى المكان الذي يشير إليه المؤشر s1، ونحصل على سلوك غير محدد إذا كان الكائنان متداخلان overlapping objects. تعيد الدالة s1.
  • دالة memmove: هذه الدالة مطابقة لعمل دالة memcpy إلا أنها تعمل على الكائنات المتداخلة، إلا أنها قد تكون أبطأ.
  • دالتَي strcpy وstrncpy: تنسخ كلا الدالتين السلسلة النصية التي يشير إليها المؤشر s2 إلى سلسلة نصية يشير المؤشر s1 إليها متضمنًا ذلك المحرف الفارغ في نهاية السلسلة. تنسخ strncpy سلسلةً نصيةً بطول n بايت على الأكثر، وتحشو ما تبقى بمحارف فارغة إذا كانت s2 أقصر من n محرف، ونحصل على سلوك غير معرّف، إذا كانت السلسلتان متقاطعتين، وتُعيد كلا الدالتين s1.
  • الدالتان strcat و strncat: تُضيف كلا الدالتين السلسلة النصية s2 إلى السلسلة s1 بالكتابة فوق overwrite المحرف الفارغ في نهاية السلسلة s1، بينما يُضاف المحرف الفارغ دائمًا إلى نهاية السلسلة. يمكن إضافة n محرف على الأكثر من السلسلة s2 باستخدام الدالة strncat مما يعني أن السلسلة النصية الهدف (أي s1) يجب أن تحتوي على مساحة لطولها الأصلي (دون احتساب المحرف الفارغ) زائد n+1 محرف للتنفيذ الآمن. تعيد الدالتين s1.

مقارنة السلسلة النصية والبايت

تُستخدم هذه الدوال في مقارنة مصفوفات من البايتات، وهذا يتضمن طبعًا السلاسل النصية في لغة سي إذ أنها سلسلةٌ من المحارف char (أي البايتات) بمحرف فارغ في نهايتها. تعمل جميع هذه الدوال التي سنذكرها على مقارنة بايت تلو الآخر وتتوقف فقط في حالة اختلف بايت مع بايت آخر (في هذه الحالة تُعيد الدالة إشارة الفرق بين البايت والآخر) أو عندما تكون المصفوفتان متساويتين (أي لم يُعثر على أي فرق بينهما وكان طولهما مساوٍ إلى الطول المحدد أو -في حالة المقارنة بين السلاسل النصية- وُجد المحرف الفارغ في النهاية).

القيمة المُعادة في جميع الدوال عدا الدالة strxfrm هي أصغر من الصفر، أو تساوي الصفر، أو أكبر من الصفر وذلك في حالة كان الكائن الأول أصغر من الكائن الثاني، أو يساوي الكائن الثاني، أو أكبر من الكائن الثاني على الترتيب.

#include <string.h>

int memcmp(const void *s1, const void *s2, size_t n);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
size_t strxfrm(char *to, const char *from,
int strcoll(const char *s1, const char *s2);
  • دالة memcmp: تُقارن أول n محرف في الكائن الذي يشير إليه المؤشر s1 وs2، إلا أن مقارنة الهياكل بهذه الطريقة ليست مثالية، إذ قد تحتوي الاتحادات unions أو "الثقوب holes" المُسببة بواسطة محاذاة ذاكرة التخزين على بيانات غير صالحة.
  • دالة strcmp: تُقارن سلسلتين نصيتين وهي إحدى أكثر الدوال استخدامًا عند التعامل مع السلاسل النصية.
  • الدالة strncmp: تُطابق عمل الدالة strcmp إلا أنها تقارن n محرف على الأكثر.
  • الدالة strxfrm: تُحوّل السلسلة النصية المُمرّرة إليها (بصورةٍ خاصة ومميزة)، وتُخزّن إلى موضع المؤشر، ويُكتب maxsize محرف على الأكثر إلى موضع المؤشر (متضمنًا المحرف الفارغ في النهاية)، وتضمن طريقة التحويل أننا سنحصل على نتيجة المقارنة ذاتها لسلسلتين نصيتين محوّلتين ضمن إعدادات المستخدم المحلية عند استخدام الدالة strcmp بعد تطبيق الدالة strcoll على السلسلتين النصيتين الأساسيتين.

نحصل على طول السلسلة النصية الناتجة مثل قيمة مُعادة في جميع الحالات (باستثناء المحرف الفارغ في نهاية السلسلة)، وتكون محتويات المؤشر to غير معرّفة إذا كانت القيمة مساوية أو أكبر من maxsize، وقد تكون s1 مؤشرًا فارغًا إذا كانت maxsize صفر، ونحصل على سلوك غير معرّف إذا كان الكائنان متقاطعين.

  • دالة strcoll تُقارن هذه الدالة سلسلتين نصيتين بحسب سلسلة الترتيب collating sequence المحدد في إعدادات اللغة المحلية.

دوال بحث المحارف والسلاسل النصية

يتضمن التصنيف الدوال التالية:

#include <string.h>

void *memchr(const void *s, int c, size_t n);
char *strchr(const char *s, int c);
size_t strcspn(const char *s1, const char *s2);
char *strpbrk(const char *s1, const char *s2);
char *strrchr(const char *s, int c);
size_t strspn(const char *s1, const char *s2);
char *strstr(const char *s1, const char *s2);
char *strtok(const char *s1, const char *s2);
  • دالة memchr: تُعيد مؤشرًا يشير إلى أول ظهور ضمن أول n محرف من s* للمحرف c (من نوع unsigned char)، وتُعيد فراغًا null إن لم يُعثر على أي تطابق.
  • دالة strchr: تُعيد مؤشرًا يشير إلى أول ظهور للمحرف c ضمن s* ويتضمن البحث المحرف الفارغ، وتُعيد فراغًا null إذا لم يُعثر على أي تطابق.
  • دالة strcspn: تُعيد طول الجزء الأولي للسلسلة النصية s1 الذي لا يحتوي أيًّا من محارف السلسلة s2، ولا يؤخذ المحرف الفارغ في نهاية السلسلة s2 بالحسبان.
  • دالة strpbrk: تُعيد مؤشرًا إلى أول محرف ضمن s1 يطابق أي محرف من محارف السلسلة s2 أو تُعيد فراغًا إن لم يُعثر على أي تطابق.
  • دالة strrchr: تُعيد مؤشرًا إلى آخر محرف ضمن s1 يطابق المحرف c آخذة بالحسبان المحرف الفارغ على أنه جزء من السلسلة s1 وتُعيد فراغ إن لم يُعثر على تطابق.
  • دالة strspn: تُعيد طول الجزء الأولي ضمن السلسلة s1 الذي يتألف كاملًا من محارف السلسلة s1.
  • دالة strstr: تُعيد مؤشرًا إلى أول تطابق للسلسلة s2 ضمن السلسلة s1 أو تُعيد فراغ إن لم يُعثر على تطابق.
  • دالة strtok: تقسّم السلسلة النصية s1 إلى "رموز tokens" يُحدّد كل منها بمحرف من محارف السلسلة s2 وتُعيد مؤشرًا يشير إلى الرمز الأول أو فراغًا إن لم يوجد أي رموز. تُعيد استدعاءات لاحقة للدالة باستخدام ‎(char *)0 قيمةً للوسيط s1 الرمز التالي ضمن السلسلة، إلا أن s2 (المُحدِّد) قد يختلف عند كل استدعاء، ويُعاد مؤشر فارغ إذا لم يبق أي رمز.

دوال متنوعة أخرى

هناك بعض الدوال الأخرى التي لا تنتمي لأي من التصنيفات السابقة:

void *memset(void *s, int c, size_t n);
char *strerror(int errnum);
size_t strlen(const char *s);
  • دالة memset: تضبط n بايت يشير إليها المؤشر s إلى قيمة المحرف c وهو من النوع unsigned char، وتُعيد الدالة المؤشر s.
  • دالة strlen: تُعيد طول السلسلة النصية s دون احتساب المحرف الفارغ في نهاية السلسلة، وهي دالة شائعة الاستخدام.
  • دالة strerror: تُعيد مؤشرًا يشير إلى سلسلة نصية تصف الخطأ رقم errnum، وقد تُعدَّل هذه السلسلة النصية عن طريق استدعاءات لاحقة للدالة sterror، وتعدّ هذه الدالة مفيدةٌ لمعرفة معنى قيم errno.

التاريخ والوقت

تتعامل هذه الدوال إما مع الوقت المُنقضي elapsed time، أو وقت التقويم calendar time ويحتوي ملف الترويسة <time.h> على تصريح كلا النوعين من الدوال بالاعتماد على التالي:

  • القيمة CLOCKS_PER_SEC: عدد الدقات ticks في الثانية المُعادة من الدالة clock.
  • النوعين clock_t و time_t: أنواع حسابية تُستخدم لتمثيل تنسيقات مختلفة من الوقت.
  • الهيكل struct tm: يُستخدم لتخزين القيمة المُمثّلة لأوقات التقويم، ويحتوي على الأعضاء التالية:
int tm_sec      // الثواني بعد الدقيقة من 0 إلى 61، وتسمح 61 بثانيتين كبيستين‫ leap-second
int tm_min      // الدقائق بعد الساعة من 0 إلى 59
int tm_hour     // الساعات بعد منتصف الليل من 0 إلى 23
int tm_mday     //اليوم في الشهر من 1 إلى 31
int tm_mon      // الشهر في السنة من 0 إلى 11
int tm_year     // السنة الحالية من 1900
int tm_wday     // الأيام منذ يوم الأحد من 0 إلى 6
int tm_yday     // الأيام منذ الأول من يناير من 0 إلى 365
int tm_isdst    // مؤشر التوقيت الصيفي 

يكون العنصر tm_isdst موجبًا إذا كان التوقيت الصيفي daylight savings فعّالًا، وصفر إن لم يكن كذلك، وسالبًا إن لم تكن هذه المعلومة متوفرة.

إليك دوال التلاعب بالوقت:

#include <time.h>

clock_t clock(void);
double difftime(time_t time1, time_t time2);
time_t mktime(struct tm *timeptr);
time_t time(time_t *timer);
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timer);
struct tm *gmtime(const time_t *timer);
struct tm *localtime(const time_t *timer);
size_t strftime(char *s, size_t maxsize,
  const char *format,
  const struct tm *timeptr);

تتشارك كل من الدوال asctime و ctime و gmtime و localtime و strftime بهياكل بيانات ساكنة static من نوع struct tm أو من نوع char []‎، وقد يتسبب استدعاء أحد منها بعملية الكتابة فوق البيانات المخزنة بسبب استدعاء سابق لإحدى الدوال الأخرى، ولذلك يجب على مستخدم الدالة نسخ المعلومات إذا كان هذا سيسبب أية مشاكل.

  • الدالة clock: تُعيد أفضل تقريب للوقت الذي انقضى منذ تشغيل البرنامج مقدرًا بدقات الساعة ticks، وتُعاد القيمة ‎(clock_t)-1 إذا لم يُعثر على أي قيمة. من الضروري العثور على الفرق بين وقت بداية تشغيل البرنامج والوقت الحالي إذا أردنا إيجاد الوقت المُنقضي اللازم لتشغيل البرنامج، وهناك ثابتٌ معرفٌ حسب التنفيذ يعدل على القيمة المُعادة من clock. يجب تقسيم القيمة على CLOCKS_PER_SEC لتحديد الوقت بالثواني.
  • الدالة difftime: تُعيد الفرق بين وقت تقويم ووقت تقويم آخر بالثواني.
  • الدالة mktime: تُعيد وقت تقويم يوافق القيم الموجودة في هيكل يشير إلى المؤشر timeptr، أو تُعيد القيمة ‎(time_t)-1 إذا لم يكن من الممكن تمثيل القيمة. يُتجاهل العضوان tm_wday و tm_yday، ولا تُقيَّد باقي الأعضاء بقيمهم الاعتيادية، إذ يُضبط أعضاء الهيكل إلى قيم مناسبة ضمن النطاق الاعتيادي عند التحويل الناجح، وهذه الدالة مفيدة للعثور على التاريخ والوقت الموافق لقيمة من نوع time_t.
  • الدالة time: تُعيد أفضل تقريب لوقت التقويم الحالي باستخدام ترميز غير محدد unspecified encoding، وتُعيد القيمة ‎(time_t)-1 إذا كان الوقت غير متوفر.
  • الدالة asctime: تُحول الوقت ضمن هيكل يشير إليه المؤشر timptr إلى سلسلة نصية بالتنسيق التالي:
Sun Sep 16 01:03:52 1973\n\0

المثال السابق مأخوذٌ من المعيار، إذ يعرِّف المعيار الخوارزمية المستخدمة أيضًا، إلا أنه من المهم ملاحظة أن جميع الحقول ضمن السلسلة النصية ذات عرض ثابت وينطبق استخدامها على المجتمعات التي تتحدث باللغة الإنجليزية فقط. تُخزّن السلسلة النصية في هيكل ساكن static structure ويُمكن إعادة كتابته عن طريق استدعاءات لاحقة لأحد دوال التلاعب بالوقت (المذكورة أعلاه).

  • الدالة ctime: تكافئ عمل asctime(localtime(timer))‎. اقرأ عن الدالة asctime لمعرفة القيمة المُعادة.
  • الدالة gmtime: تُعيد مؤشرًا إلى struct tm، إذ يُضبط هذا المؤشر إلى وقت التقويم الذي يشير إليه المؤشر timer، ويُمثل الوقت بحسب شروط التوقيت العالمي المُنسّق Coordinated Universal Time -أو اختصارًا UTC-، أو المسمى سابقًا بتوقيت جرينتش Greenwich Mean Time، ونحصل على مؤشر فارغ إذا كان توقيت UTC غير مُتاح.
  • الدالة localtime: تحوّل الوقت الذي يشير إليه المؤشر timer إلى التوقيت المحلي وتُخزن النتيجة في struct tm وتُعيد مؤشرًا يشير إلى ذلك الهيكل.
  • الدالة strftime: تملأ مصفوفة المحارف التي يشير إليها المؤشر s بـمقدار maxsize محرف على الأكثر، وتُستخدم السلسلة النصية format لتنسيق الوقت المُمثّل في الهيكل الذي يشير إليه المؤشر timeptr، تُنسخ المحارف الموجودة في سلسلة التنسيق النصية (متضمنة المحرف الفارغ في نهاية السلسلة) دون أي تغيير إلى المصفوفة إلا إن كان وجِد توجيه تنسيق من التوجيهات التالية، فعندها تُسنخ القيمة المُحددة ضمن الجدول إلى المصفوفة الهدف بما يوافق الإعدادات المحلية.
‎%a اسم يوم الأسبوع باختصار
‎%A اسم يوم الأسبوع كاملًا
‎%b اسم الشهر باختصار
‎%B اسم الشهر كاملًا
‎%c تمثيل التاريخ والوقت
‎%d تمثيل يوم الشهر عشريًا من 01 إلى 31
‎%H الساعة من 00 إلى 23 (تنسيق 24 ساعة)
‎%I الساعة من 01 إلى 12 (تنسيق 12 ساعة)
‎%j يوم السنة من 001 إلى 366
‎%m الشهر من 01 إلى 12
‎%M الدقيقة من 00 إلى 59
‎%p مكافئة PM أو AM المحلي
‎%S الثانية من 00 إلى 61
‎%U ترتيب الأسبوع ضمن السنة من 00 إلى 53 (الأحد هو اليوم الأول)
‎%w يوم الأسبوع من 0 إلى 6 (الأحد مُمثّل بالرقم 0)
‎%W ترتيب الأسبوع ضمن السنة من 00 إلى 53 (الاثنين هو اليوم الأول)
‎%x تمثيل التاريخ محليًا
‎%X تمثيل الوقت محليًا
‎%y السنة دون سابقة القرن من 00 إلى 99
‎%Y السنة مع سابقة القرن
‎%Z اسم المنطقة الزمنية، لا نحصل على محارف إن لم يكن هناك أي منطقة زمنية
‎%% محرف %

يُعاد عدد المحارف الكلية المنسوخة إلى s* باستثناء محرف الفراغ في نهاية السلسلة، وتُعاد القيمة صفر إذا لم يكن هناك أي مساحة (بحسب قيمة maxsize) للمحرف الفارغ في النهاية.

ترجمة -وبتصرف- لقسم من الفصل Libraries من كتاب The C Book.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...