glib简单记录包括字符串,主循环,回调函数和xml解析

2023-05-16

一、将最近用到的glib字符串功能整理了下直接用程序记录比较好看懂

#define MAX_LEN 100
gchar * demo (char* msg, ...)
{
    gchar * pcfgfile = NULL,* para = NULL;
    va_list argp;
    va_start(argp,msg);//msg其实指的是第一个参数,这个函数是让argp指向demo实参的栈底,参数是按从右往左的顺序压入栈的,argp不包含msg
    pcfgfile = g_strdup_vprintf("%s %s %s %d",argp);
    va_end(argp);
    return pcfgfile;
}
int main(void){
    gchar * pfilename = "houjiao";
    gchar * strdouble = "11.11";
    gchar * strint = "123456;8910";
    gchar *test_str = NULL,* hj = NULL;
    gint len = 0;
    gchar buff_str[MAX_LEN+1];
    guint64 imsi = 123456789;
    gdouble test_duble = 0.0;
    gchar **str_array = NULL;

    //----------------------------------------------------------一 ------------------------------------------------------------------------
    //下面的这些函数会返回一个新的字符串缓冲区的指针,所以你在使用完后必须要释放它们。
    //--------------------------------------------------------------------------------------------------------------------------------------
    /*
    gchar *g_strdup(const gchar *str)
    复制str并返回它的一个拷贝。并非返回原来字符串的指针而是重新申请一块内存将其指针返回。*/
    hj = g_strdup(pfilename);
    test_str = g_strdup(hj);
    g_free(hj);
    hj = NULL;
    g_print("%s\n",test_str); //输出good

    /*
    gchar *strnfill(gsize length, gchar *fill_char)
    创建一个长度为length并被fill_char填充的字符串。如果fill_char为0则创建一个空字符串 */
    test_str = g_strnfill(10,0);
    g_print("%s\n",test_str);
    g_free(test_str);
    test_str = NULL;


    /*
    gchar *g_strdup_printf(const gchar *format, ...)
    像sprintf()一样格式化字符串和参数。但是,你没必要像sprintf()一样创建和指定一个缓冲区,GLib将这些自动做了。适合用于字符串拼 */
    test_str = g_strdup_printf("%s_%u",pfilename,1);
    g_print("%s\n",test_str);

    /*测试g_strdup_vprintf的拼接功能 类似上面g_strdup_printf函数的功能*/
    test_str = demo("","hj","is","number",1);//输出this is a demo!
    g_print("%s\n",test_str);
    g_free(test_str);
    test_str = NULL;

    /*
    gchar *g_strconcat(const gchar *string1, ..., NULL)
    它接受任意数量的string作为参数,并返回它们的连接后的字符串。必须将NULL作为这个函数的最后一个参数 */
    hj = g_strdup(pfilename);
    test_str = g_strconcat("   ",hj,":","good person",NULL);//输出   houjiao:good person
    g_print("%s\n",test_str);
    g_free(test_str);
    test_str = NULL;


    //----------------------------------------------------------二 ------------------------------------------------------------------------
    //在下面的函数中,你应该为返回的结果申请空间。GLib并不会返回一个拷贝给你。它们与C相对应的函数非常像,参数要包含一个足够大的缓冲区来进行字符串处理。
    //--------------------------------------------------------------------------------------------------------------------------------------
    /*
    gchar *g_stpcpy(gchar *dest, const gchar *src)
    拷贝src到dest,包括最后的NULL字符。如果它执行成功,会返回dest中结束符拷贝的指针。*/
    test_str = (gchar *)g_malloc0(strlen(hj));
    g_stpcpy(test_str,hj);//函数返回的是结束符拷贝的指针这里即返回空字符串
    g_print("%s\n",test_str);//输出,good
    g_slice_free1(strlen(hj),test_str);
    test_str = NULL;

    /*
    gint g_snprintf(gchar *string, gulong n, const gchar *format, ...)
    你必须确保string有足够大的空间。而且你必须要用n来指定这个缓冲区的大小。返回值是输出字符串的长度,也有可能这个输出字符串为了适应缓冲区的大小而被截断 */
    len += g_snprintf(buff_str+len,MAX_LEN-len,"%d,%d,%d,%"G_GINT64_FORMAT"",1,2,3,imsi);
    g_print("%s\n",buff_str);//输出1,2,3,123456789

    /*测试g_strchug删除字符串开始的空格*/
    test_str = g_strconcat("   ",hj,":","good person     ",NULL);
    test_str = g_strchug(test_str);//输出houjiao:good person     光标在这
    g_print("%s\n",test_str);

    /*
    gchar *g_strchomp(gchar *string)
    将string结尾的空格都删掉,返回string */
    test_str = g_strchomp(test_str);
    g_print("%s\n",test_str);//houjiao:good person
    g_free(test_str);
    test_str = NULL;

    /*
    gchar *g_strstrip(gchar *string)
    将string开头和结尾的空白字符都删掉,返回string。*/
    test_str = g_strconcat("   ",hj,":","good person     ",NULL);
    test_str = g_strstrip(test_str);
    g_print("%s\n",test_str);//输出houjiao:good person

    /*
    gchar *g_strdelimit(gchar *string, const gchar *delimiters, gchar *new_delimiter)
    将string中的delimiters各个字符都替换为new_delimiter这一个字符 */
    test_str = g_strdelimit(test_str,"good",'a');
    g_print("%s\n",test_str);//haujiaa:aaaa persan
    g_free(test_str);
    test_str = NULL;

    //----------------------------------------------------------三、 ------------------------------------------------------------------------
    //在下面的函数中,除了g_ascii_dtostr()之外,都不改变它们的参数。
    //--------------------------------------------------------------------------------------------------------------------------------------
    
    /*
    gchar *g_strstr_len(const gchar *haystack, gssize haystack_len, const gchar *needle)
    在haystack中遍历haystack_len长度,如果找到了needle字串,则返回这个位置的指针,如果没有找到则返回NULL。*/
    test_str = g_strstr_len(pfilename,strlen(pfilename),"jia");
    g_print("%s\n",test_str);//输出jiao
    test_str = NULL;

    /*  
    gchar *g_strrstr(const gchar *haystack, const gchar *needle)
    类似于g_strstr_len,这个函数将会从后面开始查找,但是它并没有haystack_len参数。 */
    test_str = g_strrstr(pfilename,"jia");
    g_print("%s\n",test_str);//输出jiao
    test_str = NULL;

    /*
    gchar *g_strrstr_len(gchar *haystack, gssize haystack_len, gchar *needle)
    与g_strrstr()相同,但是它只在前haystack_len个字符中查找,第二个参数是-1表示在整个字符串中找*/
    test_str = g_strrstr_len(pfilename,-1,"a");
    g_print("%s\n",test_str);
    test_str = g_strrstr_len(pfilename,5,"a");
    g_print("%s\n",test_str);//输出(null)

    /*
    gdouble g_ascii_strtod(const gchar *nptr, gchar **endptr)
    将string转为双字长度的浮点数。如果你提供了一个有效的endptr指针地址,这个函数会将指针设置到string中被转换的最后一个字符的位置*/
    test_duble = g_ascii_strtod(strdouble,NULL);
    g_print("%.2f\n",test_duble);//输出11.11

    /*
    gchar **g_strsplit(const gchar *string, const gchar *delimiter, gint max_tokens)
    使用delimiter来将string切割成至多max_tokens个部分。返回值是新申请的一个字符串数组,用来保存被切割的这些部分。
    这个字符串数组必须由你自己释放。 如果输入字符串是空的,这个返回值也是一个空的数组。 该法不改变原string */
    str_array = g_strsplit(strint,";",3);
    g_print("%s,%s\n",str_array[0],str_array[1]);//123456,8910
    
    /*
    gchar *g_str_joinv(const gchar *separator, gchar **str_array)
    将字符串数组组合成单个字符串,并将这个新申请的字符串返回。如果separator不空,g_str_joinv()会在每个字符串之间添加上一个separator分隔符。*/
    test_str = g_strjoinv(",",str_array);
    g_print("%s\n",test_str);//123456,8910    
    g_strfreev(str_array);
    str_array = NULL;
    g_free(test_str);
    test_str = NULL;

    /*测试g_ascii_strtoll将字符串转成对应整形的功能第三个参数是指的几进制*/
    test_str =strchr(strint,';');
    len = (gint)g_ascii_strtoll(strint,NULL,10);
    g_print("%d\n",len);//输出123456
    strint = test_str +1;
    len = (gint)g_ascii_strtoll(strint,NULL,10);
    g_print("%d\n",len);//输出8910
    g_free(test_str);
    test_str = NULL;
    return TRUE;
}
二、glib的主循环功能主要是为了看看主循环的串行

/*
自定义的事件源是一个继承 GSource 的结构体,即自定义事件源的结构体 的第一个成员是 GSource 结构体, 其后便可放置程序所需数据, 例如:*/
typedef struct _MySource MySource;
struct _MySource
{
    GSource source;
    gchar text[256];
};
/*
实现了事件源数据结构的定义之后,还需要实现事件源所规定的接口,主要为 prepare, check, dispatch, finalize 等事件处理函数(回调函数),它们包含于 GSourceFuncs 结构体中。
将 GSourceFuncs 结构以及事件源结构的存储空间宽度作为参数传给 g_source_new 便可构造一个新的事件源,继而可使用 g_source_attach 函数将新的事件源添加到主循环上下文中
*/
static gboolean prepare(GSource *source, gint *timeout)
{
    *timeout = 0;

    return TRUE;
}

static gboolean check(GSource *source)
{
    return TRUE;
}

static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
{
    int i = 0;
    MySource *mysource = (MySource *)source;

    for (;i<6;i++)
    {
        g_print("%s%d\n", mysource->text,i);
    }

    return TRUE;
}
/*
g_main_loop_run 函数运行时,会迭代访问 GMainContext 的事件源列表,步骤大致如下:
a. g_main_loop_run 通过调用事件源的 prepare 接口并判断其返回值以确定各事件源是否作好准备。如果各事件源的 prepare 接口的返回值为 TRUE,即表示该事件源已经作好准备,否则表示尚未做好准备。显然,上述程序所定义的事件源是已经作好了准备。
b. 若某事件源尚未作好准备 ,那么 g_main_loop 会在处理完那些已经准备好的事件后再次询问该事件源是否作好准备 ,这一过程是通过调用事件源的 check 接口而实现的,如果事件源依然未作好准备,即 check 接口的返回 FALSE,那么 g_main_loop_run 会让主事件循环进入睡眠状态。
主事件循环的睡眠时间是步骤 a 中遍历时间源时所统计的最小时间间隔 ,例如在 prepare 接口中可以像下面这样设置时间间隔。到达一定时间后, g_main_loop_run 会唤醒主事件循环,再次询问。如此周而复始,直至事件源的 prepare 接口返回值为 TRUE。*/
int main1(void)
{
    GMainLoop *loop = g_main_loop_new(NULL, TRUE);
    GMainContext *context = g_main_loop_get_context(loop);
    GSourceFuncs source_funcs = {prepare, check, dispatch, NULL};
    GSource *source = g_source_new(&source_funcs, sizeof(MySource));

    g_sprintf(((MySource *)source)->text, "Hello world");

    g_timeout_add(100, timeout_func, loop);
    g_source_attach(source, context);
    g_source_unref(source);

    g_main_loop_run(loop);

    g_main_context_unref(context);
    g_main_loop_unref(loop);

    return 0;
}
/*
该函数执行结果是大致上可以看出来事件源是串行的即单线程
*/
三、glib的xml解析功能顺便使用简单的回调函数

//程序功能:使用回调函数的方式调用xml和txt两种文件的读取函数。
xml文件里面的内容如下
<?xml version="1.0" encoding="UTF-8"?>
  <NM id="15009244480" name="houjiao">
      <title>name ege sex what emotion</title>
        <v>hq 24 female friend love</v>
        <v>ld 24 female friend love</v>
        <v>wj 25 female friend love</v>
        <v>wff 25 female friend love</v>
  </NM>
txt文件里面内容如下
hq is 24 years older,and she is my friend.
ld is 24 years older,and she is my friend.
wj is 25 years older,and she is my friend.
wff is 25 years older,and she is my friend.
然后程序如下,注释已经很清晰了

#include "glib.h"
#include "string.h"
#include "stdio.h"
//定义函数指针即回调函数的类型
typedef gboolean (*CallBackFileRead)(gchar *);
typedef struct _FriendNode
{
    gchar * myname;
    gchar * hername;
    gchar * what;
    gchar * emotion;
    guint8 ege;
    guint64 tel_id;
    gboolean txt_pasre_flag;
}FriendNode;

/*当遇到元素开始时,将其属性名全存到attribute_name数组中,对应的属性值存入value_cursor数组中,所以属性名和属性值是同时++然后就会取到下一个属性直到取完*/
static void start( GMarkupParseContext * context,
                  const gchar * element_name,
                  const gchar * * attribute_names,
                  const gchar * * attribute_values,
                  gpointer user_data,
                  GError * * error )
{
    const gchar * * name_cursor = attribute_names;
    const gchar * * value_cursor = attribute_values;
    FriendNode * friend_node =(FriendNode *)user_data ;
    if (g_strcmp0(element_name,"v") == 0)
    {    
        friend_node->txt_pasre_flag = TRUE;        
    }
    else if (g_strcmp0(element_name,"NM") == 0)
    {
        while (*name_cursor)
        {
            if (g_strcmp0(*name_cursor, "id") == 0)
            {
                friend_node->tel_id = (guint64)g_ascii_strtoull(*value_cursor,NULL,10);

            }
            else if (g_strcmp0(*name_cursor, "name") == 0)
            {
                friend_node->myname = g_strdup (*value_cursor);
            }
            name_cursor++;
            value_cursor++;
        }
        g_print("My name is %s,my telphone num is %"G_GUINT64_FORMAT" \n",friend_node->myname,friend_node->tel_id);
    }
}
//一个元素结束时调用,一般可以在你想要取值的元素设置标记。
static void end( GMarkupParseContext * context,
                const gchar * element_name,
                gpointer user_data,
                GError * * error )
{
    FriendNode * friendnode = (FriendNode*)user_data;
    if (g_strcmp0(element_name,"v") == 0)
    {
        friendnode->txt_pasre_flag = FALSE;
    }
}

//一般xml里面要取得最重要的是内容,因此按照内容格式不同,拆分字符串的方式不同。
static void text( GMarkupParseContext * context,
                 const gchar * text,
                 gsize text_len,
                 gpointer user_data,
                 GError * * error )
{
    const gchar * s = text;
    gchar * ss = NULL;
    int i=0;    //列索引
    FriendNode * friendnode = (FriendNode *)user_data;

    if (friendnode->txt_pasre_flag)
    {
        while ((ss = strchr((gchar *)s,' ')) && i<=3)
        {
            switch (i)
            {
            case 0:                
                friendnode->hername = g_strndup(s,ss-s);
                break;
            case 1:
                friendnode->ege =(gint8)g_ascii_strtoull(s,NULL,10);
                break;
            case 3:                
                friendnode->what = g_strndup(s,ss-s);
                break;

            default:
                break;
            }
            s = ss + 1;
            i++;
        }
        if (i == 4)
        {
            friendnode->emotion =g_strdup(s);
        }
        g_print("%s is %d years older,and she is my %s who I %s\n",friendnode->hername,friendnode->ege,friendnode->what,friendnode->emotion);
        g_free(friendnode->hername);
        g_free(friendnode->what);
        g_free(friendnode->emotion);
    }
}
//xml解析类回调函数的实现
gboolean ReadXmlFile(gchar * filename){
    GMarkupParser gparser = {0};               //GMarkup解析器
    GMarkupParseContext *gparsecontext = NULL; //GMarkup解析上下文
    FriendNode friend_node = {0};
    char * buf;
    gsize length;
    /*创建解析器上下文第三个参数是用来传给 GMarkupParser解析器函数即start,text和end里面用的 */
    gparsecontext = g_markup_parse_context_new(&gparser,(GMarkupParseFlags)0, &friend_node, NULL);

    if (gparsecontext == NULL)
    {
        return FALSE;
    }
    /*给gparser的函数指针赋值*/
    gparser.start_element = start;
    gparser.text = text;
    gparser.end_element =end;


    /* g_file_get_contents(const gchar *filename,gchar **contents,gsize *length,GError **error);
    是用于将文件的内容读入到一个buf中,length是用来存储字符串长度的变量可设为null,读取成功返回ture读取失败返回false和最后一个参数 */
    if ((FALSE == g_file_get_contents(filename, &buf, &length,NULL)))
    {
        return FALSE;
    }


    /*开始解析buf里面的字符串,调用gparser的三个函数,xml文件里面一个元素包括,元素名,属性值和元素内容,一个元素的start标志为:<元素名[属性]>,元素的end标志为:</元素名> 包含在元素开始和元素结束之间的变是该元素的text*/
    if (g_markup_parse_context_parse(gparsecontext, buf, length, NULL) == FALSE)
    {
        g_message("load xml file failed!");
        g_free(buf);
        g_markup_parse_context_free(gparsecontext);
        gparsecontext = NULL;
        return FALSE;
    }
}
//csv解析类回调函数实现
gboolean ReadTxtFile(gchar * filename){
    FILE * ftxt_file = NULL;
    gchar buf[100] = {'\0'};
    if (fopen_s(&ftxt_file,filename,"rb") != 0)
    {
        g_print("open file error");
        return FALSE;
    }
    //fgets遇到文件结束或读取错误时返回null
    while(fgets(buf, 100, ftxt_file))
    {
       g_print("%s",buf);
    }
    fclose(ftxt_file);
    return TRUE;
}

void XmlTxtParseCallBack(gchar* filename, CallBackFileRead pFun)  //模拟API函数  
{
    //回调解析文件类函数
    if (!pFun(filename))
    {
        g_print("parse error");
    }
}
//1.在main中的XmlTxtParseCallBack接口函数调用了回调函数,当然xml解析和txt解析的两个函数可以像普通函数一样被调用,但是当它作为参赛传递给被调函数时就称为回调函数了。
//2.回调函数实际上就是在调用某个函数(通常是API函数时),将自己的一个函数(这个函数叫回调函数)的地址作为参数传递给那个函数,那个函数在需要的时候利用传递的地址调用回调函数,这种模式很适合异步模块之间的调用。
//3.用计算机原理来说,回调函数就是一个中断处理函数,由系统在符合你设定条件时自动调用,你要做的只有三步,一声明回调函数类型,二实现回调函数,三,设置触发条件(即把你的回调函数名转化为函数指针作为参数用于调用)。
int main()
{
    gchar * filename = "F:\\test\\input\\hj.txt";
    if (g_str_has_suffix(filename,".xml"))
    {
        XmlTxtParseCallBack(filename,ReadXmlFile);
    }
    else if (g_str_has_suffix(filename,".txt"))
    {
        XmlTxtParseCallBack(filename,ReadTxtFile);
    }
    else
    {
        g_print("can't parse this file type");
    }
    return 0;

}

执行结果是:

当filename是**hj.txt时,

当file那么是**hj.xml时,

over,glib待续。。。

转载于:https://www.cnblogs.com/tears-of-ramos/p/3929175.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

glib简单记录包括字符串,主循环,回调函数和xml解析 的相关文章

随机推荐

  • 云之彼端,牵手未来—— “我思故我在”—我眼中的第四届中国云计算大会

    一 xff0e 满怀激动踏征程 第四届全国云计算大会初体验 2012年5月23日至25日 业界瞩目的第四届中国云计算大会 xff08 以下简称 大会 xff09 在京隆重举行 本次大会由国家发展和改革委员会 工业和信息化部 北京市人民政府及
  • 落花渐欲迷人眼,移动前景看用户

    火红的深秋10月 xff0c 万众瞩目的第三届中国移动开发者大会于19日在北京国家会议中心如期举行 本次大会邀请到了诸多互联网巨头公司中相关项目负责人及移动互联先驱精英 xff0c 百家争艳齐聚一堂 xff0c 共同探讨在移动互联网高速发展
  • 新员工总结

    感谢29 日下午张宁主编为我们移动频道新员工安排的培训 通过本次员工培训 xff0c 在工作目标和方向上有了较为清晰的认识 xff0c 主要总结如下 xff1a 1 明确移动频道工作重心 xff1a 移动 应用 开发 围绕这三点 xff0c
  • 微软Win8开发马拉松感悟

    前几天前往微软win8开发者马拉松大赛 xff0c 对于微软中国有了一些了解 xff0c 也有了一些体悟 xff0c 在这里稍微记录一下 首先一点就是微软对于开发者的态度 在会场看到了许多沙发和抱枕 xff0c 还有毛毯等 xff0c 另外
  • 基于prometheus的网络指标监控

    1 网络指标监控 网络设备 交换机 路由器 防火墙 本身并没有太多的监控方式 xff0c 常规的是使用snmp xff0c 通过oid去采集网络设备的指标 xff0c 例如 xff0c 流量或者错包 xff0c 不过近年来也出现了新的采集方
  • 【解决】UART问题

    UART和USART区别 UART xff08 Universal asynchronous receiver transmitter xff09 xff1a 通用异步收发器 USART xff08 Universal synchronou
  • 小白也能配置VScode的C/C++环境

    必要软件的下载 前往下方Github链接下载 xff0c 选择这里下载 下载地址 xff1a https github com SDchao AutoVsCEnv WPF releases latest 2022 9 25 更新 xff1a
  • 网络协议栈0:从一个例子开始

    最近因工作需要写一个网卡驱动 xff0c 晕倒 xff0c 没有任何网络知识 xff0c 就写网络驱动 xff01 可是 xff0c 为了五斗米糊口 xff0c 不得不从啊 于是 xff0c 打算从网络协议栈开始 xff0c 把网络搞一搞
  • Ubuntu 18.04 Intel RealSense D435i 相机标定教程

    1 D435i相机简介 RealSenseD435i 是一款立体视觉深度相机 xff0c 如下图所示 xff0c 其集成了两个红外传感器 xff08 IR Stereo Camera xff09 一个红外激光发射器 xff08 IR Pro
  • 使用IDEA创建Servlet项目

    使用 IDEA创建Servlet项目 原料 xff1a IDEA 2016 xff0c Tomcat8 0 步骤如下 xff1a 1 创建项目 File gt New gt Project gt Java Enterprise 如下图所示
  • px4基本知识

    1 PX4自驾仪 PX4是与平台无关的自动驾驶仪软件 xff08 或称为固件 xff09 xff0c 可以驱动无人机或无人车 它可以被烧写在某些硬件 xff08 如Pixhawk v2 xff09 xff0c 并与地面控制站在一起组成一个完
  • React尚硅谷115-126(setState、Hooks、Fragment、context、组件优化、renderprops、错误边界、组件通信方式总结)

    115 项目打包运行 npm install serve g serve build 116 setState setState是同步的 xff0c 但是引起页面更新动作是异步的 xff08 状态的更新是异步的 xff09 相当于this
  • 程序员跨越式成长指南

    我的 GitChat 达人课 程序员跨越式成长指南 xff0c 更新了三篇文章 xff0c 现在完成的内容如下 xff1a 导读 xff1a 开发路上的两次关键跨越 第01课 xff1a 如何做到选择大于努力 第02课 xff1a 开发者的
  • 树莓派一些传感器的使用

    树莓派一些传感器的使用 Part 1 树莓派GPIO编号方式和引脚说明 参考 xff1a 树莓派开发系列教程9 树莓派GPIO控制 1 功能物理引脚 从左到右 xff0c 从上到下 xff0c 左边基数 xff0c 右边偶数 xff0c 1
  • centos 安装Git-cola

    首先安装Git sudo yum y install git 找到 git all noarch 安装这个 sudo yum install git all noarch 61 61 61 61 61 61 61 61 61 61 61 6
  • C++继承总结

    继承的概念 继承机制是面向对象程序设计使代码可以复用的重要手段 xff0c 它允许程序员在保持原有类特性的基础之上进行其他扩展 xff0c 增加功能 xff0c 这样产生的新类 xff0c 称之为派生类 继承呈现了面向对象程序设计的层次结构
  • 读完《大数据时代》的一点儿心得

    工作一段时间之后 xff0c 总喜欢读读书 xff0c 这是多年养成下来的一个习惯 读书使人避恶 xff0c 读书使人向善 xff0c 读书使人聪慧 xff0c 读书使人高尚 xff0c 我们都是聪明人 xff0c 对吧 xff1f 哈哈哈
  • 产品管理精华:第十一,数据的思维方式

    xfeff xfeff 数量庞大 增长迅猛 种类多样的数据已经成为企业在大数据时代发展不得不面临的现实境况 这是挑战 xff0c 也是机遇 基于客户需求 xff0c 提供领先 全面 有效的解决方案 xff0c 帮助企业获取商业洞察力 xff
  • 产品管理精华:第十四,产品炼成?

    xfeff 最近一段时间在准备信息系统项目管理师 的考试 xff0c 在备考复习过程中 xff0c 通过系统学习和理解项目管理相关知识 xff0c 发现项目和产品管理联系很密切 项目过程是伴随着产品过程而生 我们先来了解一下项目的定义 xf
  • glib简单记录包括字符串,主循环,回调函数和xml解析

    一 将最近用到的glib字符串功能整理了下直接用程序记录比较好看懂 define MAX LEN 100 gchar demo char msg gchar pcfgfile 61 NULL para 61 NULL va list arg