تحليل ملفات ال XML في لغة ال c
المعرفة المسبقة بال XML حتي ولو مستوي مبتدأ مطلوبه
حنستخدم في شرحنا مكتبة ال GLIB النهرده , مع العلم ان في اكتر من مكتبة تانيه مكتوبين بال ANSI C عشان تقدر بيهم تحلل ملفات ال xml زي libxml و expat دور عليهم في جوجل
بس السبب الي خلاني استخدم glib في الموضوع دوت هو اني هشرح جزء من برنامج انا حاليا بكتبه
بستخدم فيه glib و gtk+ فطبيعي اني مش لازم ادمج مكتبة تانيه طالما الامكانيه متوافرة !
ودلوقتي
هنبدأ بتعريف ال objects الي هنقوم باستخدامها
GMarkupParser;
GMarkupParseContext;
[/rtl]
اولهم structure بتحتوي علي الاتي
[/rtl]
typedef struct {
/* Called for open tags <foo bar="baz"> */
void (*start_element) (GMarkupParseContext *context,
const gchar *element_name,
const gchar **attribute_names,
const gchar **attribute_values,
gpointer user_data,
GError **error);
/* Called for close tags </foo> */
void (*end_element) (GMarkupParseContext *context,
const gchar *element_name,
gpointer user_data,
GError **error);
/* Called for character data */
/* text is not nul-terminated */
void (*text) (GMarkupParseContext *context,
const gchar *text,
gsize text_len,
gpointer user_data,
GError **error);
/* Called for strings that should be re-saved verbatim in this same
* position, but are not otherwise interpretable. At the moment
* this includes comments and processing instructions.
*/
/* text is not nul-terminated. */
void (*passthrough) (GMarkupParseContext *context,
const gchar *passthrough_text,
gsize text_len,
gpointer user_data,
GError **error);
/* Called on error, including one set by other
* methods in the vtable. The GError should not be freed.
*/
void (*error) (GMarkupParseContext *context,
GError *error,
gpointer user_data);
} GMarkupParser;
دوت متاخد من الموقع الرسمي بتاع glib , الي هيهمنا منهم حاجتين
start_element , end_element
بيتم استدعاء start element لما ال parser بيقابل اي opening tag في ال markup
ومش محتاج طبعا اعرف ال end_element وظيفته ايه

بس ماشي بيتم استدعائه عند انتهاء ال element ده
جميل الكلام , ال function ديت احنا الي بنكتبها وال prototype بتاعها بيكون مطابق للي موجود بداخل ال structure ديت
هنتكلم عنهم بعدين بعد منتكلم عن ال object التاني
ال object دوت هو الي مسؤل عن استدعاء ال parser علي stream معين من ال bytes بيحتوي علي xml
بمعني اصح لما بيقابل new element بيقوم باستدعاء element_start وهكذا
نبدأ بانشاء ال context دوت
#inlcude <glib.h>
GMarkupParseContext *context;
context = g_markup_parse_context_new(&p
,0
,NULL
,NULL);
نبدا بالشرح , اول parameter هو GMarkupParser pointer
تاني واحد مجرد flags تقتدر تستخدمها عشان تنوه ال context باشياء معينه انت عاوزها , ملهاش استخدام معانا
تالت متغير هو user_data بيتم تمريرها لل parser كل مره بيتم استدعاء احدي ال functions بتاعته
رابعا واخيرا GError ** ودوت بيتم استخدامه لعمل allocate ل error structure عشان تقدر تعرف لو حصل اي نوع من انواع الاخطاء انك تستخرج ال string منها
وتقدر تبلغ اليوزر او تتصرف علي اساس ذلك انت حر
ثانيا ال parser object
#include <glib.h>
GMarkupParser p;
p.start_element = startEl;
p.end_element = endEl;
p.text = NULL;
p.passthrough = NULL;
p.error = NULL;
startEl و Endel ماهم الا ال functions الي احنا هنستخدمها عشان نتعامل مع ال elements هيتم عرضها قريبا
دلوقتي انت جهاز لاستخدام ال xml parser
الكود كامل
#include <glib.h>
#include <stdio.h>
#include <string.h>
static void start(GMarkupParseContext *context
,const gchar *name
,const gchar **attsn
,const gchar **attsv
,gpointer user_data
,GError **error)
{
gint x;
if(strstr(name , "current_conditions"))
{
printf("%s\n",name);
printf("-------------------\n");
}
else if(strstr(name , "temp_c"))
{
printf("%s = '%s'\n",name , attsv[0]);
}
else if(strstr(name , "temp_f"))
{
printf("%s = '%s'\n",name , attsv[0]);
}else if(strstr(name , "day_of_week"))
{
printf("-------------------\n");
printf("%s = '%s'\n", name ,attsv[0]);
}
else if(strstr(name , "low"))
{
printf("%s = '%s'\n",name , attsv[0]);
}
else if(strstr(name , "high"))
{
printf("%s = '%s'\n",name , attsv[0]);
}
}
static void end(GMarkupParseContext *context
,const gchar *name
,gpointer user_data
,GError **error)
{
;
}
int main(int argc , char **argv)
{
FILE *fp = fopen(argv[1],"r");
GMarkupParser p;
GMarkupParseContext *context;
gint depth = 0;
gchar buffer[BUFSIZ];
p.start_element = start;
p.end_element = end;
p.text = NULL;
p.passthrough = NULL;
p.error = NULL;
context = g_markup_parse_context_new(&p
,0
,&depth
,NULL);
while(fgets(buffer , BUFSIZ , fp) != NULL)
{
int len = strlen(buffer);
len = len-1;
//printf("%s\n",buffer);
g_markup_parse_context_parse(context , buffer , len ,NUL L);
}
g_markup_parse_context_free(context);
return 0;
}
كل الكود مفهوم عدا بعض الاجزاء الي هبدأ بشرحها
اولا انا هنا استخدم صفحة من google weather api عشان اقدر استخرج معلومات الطقس
تقدر تحصل عليها من
هنا ومن ثم قمت بعمل Parser و context لل parser دوت عشان اتعامل مع ال xml buffers
ثانيا قمت بتعريف startEl بالشكل التالي
static void start(GMarkupParseContext *context
,const gchar *name
,const gchar **attsn
,const gchar **attsv
,gpointer user_data
,GError **error);
اول متغير بيم تمريره هو ال context الي اتكلمنا عنه , تاني متغير هو اسم ال element الي احنا بنعمله parse حاليا , ثالت متغير ورابع متغير هي array of strings لل attributes وال values بتاعتها , الخامس هو ال user_data الي اتكلمنا عنها , في مثالنا ملهاش استخدام لكن هقدملك مثال تاني بيستتخدمها عشان يطبع ملف ال xml بصوره كويسه وترتيب كويس
اخر متغير كما قلنا من قبل هو ال GError structure عشان لو فيه اي اخطاء تقدر تكتبها بداخلها , التعامل معاها خارج نطاق الدورة بتاعتنا
تاني دالة هي ال endEl ولو تلاحظ اني مستخدمتهاش في اي حاجه , لاني مسحت الكود الخاص بيها عشان التعقيد
static void end(GMarkupParseContext *context
,const gchar *name
,gpointer user_data
,GError **error);
اول متغير طبعا عرفناه , تاني متغير هو اسم العنصر الي انتهينا منه ,. الربع والخامس زي الي فاتت بالظبت
الكود بيقوم بقرائة buffer مكونة من 1024 byte وبيقوم بتمريرها لل context الي بياخد ال parser اول متغير ليه وال buffer وحجمه تاني وتالت متغير ليه , ورابع متغير هو ال GError
وبيقوم بعمل parse لل buffer دوت , طبعا انا تجاوزت التحقق من الاخطاء ولكن لابد من التحقق منها في كل دالة , راجع ال reference في اخر الموضوع .
عن كل بداية عنصر بيتم استدعاء ال start_element الي بدورها بتشير لل function بتاعتنا
وعند ذلك بيتم تنفيذها
وعن انتهاء العنصر بيتم استدعاء ال end_element وطبعا الي بدورها بتشير لل function بتاعت ناهاية ال element
هنستخدم مثال تاني دلوقتي وهسنتخدم فيه ال user_data
#include <glib.h>
#include <stdio.h>
#include <string.h>
static void start(GMarkupParseContext *context
,const gchar *name
,const gchar **attsn
,const gchar **attsv
,gpointer user_data
,GError **error)
{
gint *i = (gint *)user_data;
gint x;
for(x = 0; x < *i ; x++)
printf("\t");
printf("%s\n",name);
*i += 1;
}
static void end(GMarkupParseContext *context
,const gchar *text
,gpointer user_data
,GError **error)
{
gint *i = (gint *)user_data;
*i -= 1;
}
int main(int argc , char **argv)
{
FILE *fp = fopen(argv[1],"r");
GMarkupParser p;
GMarkupParseContext *context;
gint depth = 0;
gchar buffer[BUFSIZ];
p.start_element = start;
p.end_element = end;
p.text = NULL;
p.passthrough = NULL;
p.error = NULL;
context = g_markup_parse_context_new(&p
,0
,&depth
,NULL);
while(fgets(buffer , BUFSIZ , fp) != NULL)
{
int len = strlen(buffer);
len = len-1;
//printf("%s\n",buffer);
g_markup_parse_context_parse(context , buffer , len ,NULL);
}
g_markup_parse_context_free(context);
return 0;
}
استخدمنا متغير بسيط مررناه ك user_data عشان نقدر من خلاله نقدر نعرف ال depth بتاع كل element وبما ان كل element بيبدأ بيعمل start جديده يبقي قيمة المتغير دوت هتكبر
ولما ينتهي قيمتة هتقل
وبذلك نقدر نعرف العمق بتاع كل element . ومن خلال دوت نقدر نطبع عدد معين من ال tabs او ال spaces الي من خلالها نقدر نخلي شكل ال document احسن
او نقدر نستخدم اي data تانيه مكان ال user_data لاي اسباب اخري
وبكده المقالة ديت تكون انتهت اتمني ان حد يستفاد منها