第十章 文本处理¶
10.1 状态机¶
#include <ctype.h>
#include <stdio.h>
#define IS_WORD_CHAR(c) (isalpha(c) || isdigit(c))
int count_word(const char* text)
{
enum _State
{
STAT_INIT,
STAT_IN_WORD,
STAT_OUT_WORD,
}state = STAT_INIT;
int count = 0;
const char* p = text;
for(p = text; *p != '\0'; p++){
switch(state){
case STAT_INIT:{
if(IS_WORD_CHAR(*p)){
state = STAT_IN_WORD;
}else{
state = STAT_OUT_WORD;
}
break;
}
case STAT_IN_WORD:{
if(!IS_WORD_CHAR(*p)){
count++;
state = STAT_OUT_WORD;
}
break;
}
case STAT_OUT_WORD:{
if(IS_WORD_CHAR(*p)){
state = STAT_IN_WORD;
}
break;
}
default:break;
}
}
if(state == STAT_IN_WORD){
count++;
}
return count;
}
#ifdef COUNT_WORD_TEST
#include <assert.h>
int main(int argc, char* argv[])
{
assert(count_word("") == 0);
assert(count_word(" ") == 0);
assert(count_word("it") == 1);
assert(count_word("it ") == 1);
assert(count_word("it is") == 2);
assert(count_word("it is used to count words.") == 6);
assert(count_word("it is used to count words. is it easy?") == 9);
return 0;
}
#endif/*COUNT_WORD_TEST*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define IS_WORD_CHAR(c) (isalpha(c) || isdigit(c))
typedef void (*OnWordFunc)(void* ctx, const char* word);
int word_segmentation(const char* text, OnWordFunc on_word, void* ctx)
{
enum _State{
STAT_INIT,
STAT_IN_WORD,
STAT_OUT_WORD,
}state = STAT_INIT;
int count = 0;
char* copy_text = strdup(text);
char* p = copy_text;
char* word = copy_text;
for(p = copy_text; *p != '\0'; p++){
switch(state){
case STAT_INIT:{
if(IS_WORD_CHAR(*p)){
word = p;
state = STAT_IN_WORD;
}
break;
}
case STAT_IN_WORD:{
if(!IS_WORD_CHAR(*p)){
count++;
*p = '\0';
on_word(ctx, word);
state = STAT_OUT_WORD;
}
break;
}
case STAT_OUT_WORD:{
if(IS_WORD_CHAR(*p)){
word = p;
state = STAT_IN_WORD;
}
break;
}
default:break;
}
}
if(state == STAT_IN_WORD){
count++;
on_word(ctx, word);
}
free(copy_text);
return count;
}
#ifdef WORD_SEGMENTATION_TEST
#include <assert.h>
void on_word(void* ctx, const char* word)
{
printf("%s\n", word);
return;
}
int main(int argc, char* argv[])
{
assert(word_segmentation("it is used to word segmentation. is it easy?", on_word, NULL) == 9);
return 0;
}
#endif/*WORD_SEGMENTATION_TEST*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef void (*OnTokenFunc)(void* ctx, int index, const char* token);
#define IS_DELIM(c) (strchr(delims, c) != NULL)
int parse_token(const char* text, const char* delims, OnTokenFunc on_token, void* ctx)
{
enum _State{
STAT_INIT,
STAT_IN,
STAT_OUT,
}state = STAT_INIT;
int count = 0;
char* copy_text = strdup(text);
char* p = copy_text;
char* token = copy_text;
for(p = copy_text; *p != '\0'; p++){
switch(state){
case STAT_INIT:
case STAT_OUT:{
if(!IS_DELIM(*p)){
token = p;
state = STAT_IN;
}
break;
}
case STAT_IN:{
if(IS_DELIM(*p)){
*p = '\0';
on_token(ctx, count++, token);
state = STAT_OUT;
}
break;
}
default:break;
}
}
if(state == STAT_IN){
on_token(ctx, count++, token);
}
on_token(ctx, -1, NULL);
free(copy_text);
return count;
}
#ifdef PARSE_TOKEN_TEST
#include <assert.h>
void on_token(void* ctx, int index, const char* token)
{
printf("[%d] %s\n", index, token);
return;
}
int main(int argc, char* argv[])
{
assert(parse_token("it is used to token segmentation. is it easy?", " .?", on_token, NULL) == 9);
assert(parse_token("/backup/tools/jdk1.5.0_18/bin/"":/usr/lib/qt-3.3/bin:/usr/kerberos/bin:"
"/backup/tools/jdk1.5.0_18/bin/:/usr/lib/ccache:/usr/local/bin:/usr/bin:"
"/bin:/home/lixianjing/bin", ":", on_token, NULL) == 9
);
assert(parse_token("/backup/tools/jdk1.5.0_18/bin/", "/", on_token, NULL) == 4);
return 0;
}
#endif/*COUNT_TEST*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
const char* strtrim(char* str)
{
char* p = NULL;
p = str + strlen(str) - 1;
while(p != str && isspace(*p)){
*p = '\0';
p--;
}
p = str;
while(*p != '\0' && isspace(*p)) p++;
if(p != str){
char* s = p;
char* d = str;
while(*s != '\0'){
*d = *s;
d++;
s++;
}
*d = '\0';
}
return str;
}
static void ini_parse_internal(char* buffer, char comment_char, char delim_char)
{
char* p = buffer;
char* group_start = NULL;
char* key_start = NULL;
char* value_start = NULL;
enum _State{
STAT_NONE = 0, /*空白状态*/
STAT_GROUP,
STAT_KEY,
STAT_VALUE,
STAT_COMMENT /*注释状态*/
}state = STAT_NONE;
for(p = buffer; *p != '\0'; p++){
switch(state){
case STAT_NONE:{
if(*p == '['){
state = STAT_GROUP;
group_start = p + 1;
}else if(*p == comment_char){
state = STAT_COMMENT;
}else if(!isspace(*p)){
state = STAT_KEY;
key_start = p;
}
break;
}
case STAT_GROUP:{
if(*p == ']'){
*p = '\0';
state = STAT_NONE;
strtrim(group_start);
printf("[%s]\n", group_start);
}
break;
}
case STAT_COMMENT:{
if(*p == '\n'){
state = STAT_NONE;
break;
}
break;
}
case STAT_KEY:{
if(*p == delim_char || (delim_char == ' ' && *p == '\t')){
*p = '\0';
state = STAT_VALUE;
value_start = p + 1;
}
break;
}
case STAT_VALUE:{
if(*p == '\n' || *p == '\r'){
*p = '\0';
state = STAT_NONE;
strtrim(key_start);
strtrim(value_start);
printf("%s%c%s\n", key_start, delim_char, value_start);
}
break;
}
default:break;
}
}
if(state == STAT_VALUE)
{
strtrim(key_start);
strtrim(value_start);
printf("%s%c%s\n", key_start, delim_char, value_start);
}
return;
}
#ifdef INI_PARSER_TEST
int main(int argc, char* argv[])
{
char* buffer = strdup("[lixianjing]\nname=lixianjing\ngender=male\n[zhangshan]\nname=zhangshan\ngender=male\nage=100");
ini_parse_internal(buffer, '#', '=');
free(buffer);
return 0;
}
#endif/*INI_PARSER_TEST*/
#ifndef XML_PARSER_H
#define XML_PARSER_H
struct _XmlParser;
typedef struct _XmlParser XmlParser;
XmlParser* xml_parser_create(void);
void xml_parser_parse(XmlParser* thiz, const char* xml);
void xml_parser_destroy(XmlParser* thiz);
#endif/*XML_PARSER_H*/
/*
* File: xml_parser.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "xml_parser.h"
#define MAX_ATTR_NR 64
struct _XmlParser
{
const char* read_ptr;
int attrs_nr;
char* attrs[2*MAX_ATTR_NR+1];
char* buffer;
int buffer_used;
int buffer_total;
};
static const char* strtrim(char* str);
static void xml_parser_parse_entity(XmlParser* thiz);
static void xml_parser_parse_start_tag(XmlParser* thiz);
static void xml_parser_parse_end_tag(XmlParser* thiz);
static void xml_parser_parse_comment(XmlParser* thiz);
static void xml_parser_parse_pi(XmlParser* thiz);
static void xml_parser_parse_text(XmlParser* thiz);
static void xml_parser_reset_buffer(XmlParser* thiz);
XmlParser* xml_parser_create(void)
{
return (XmlParser*)calloc(1, sizeof(XmlParser));
}
void xml_parser_parse(XmlParser* thiz, const char* xml)
{
enum _State{
STAT_NONE,
STAT_AFTER_LT,
STAT_START_TAG,
STAT_END_TAG,
STAT_TEXT,
STAT_PRE_COMMENT1,
STAT_PRE_COMMENT2,
STAT_COMMENT,
STAT_PROCESS_INSTRUCTION,
}state = STAT_NONE;
thiz->read_ptr = xml;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
char c = thiz->read_ptr[0];
switch(state){
case STAT_NONE:{
if(c == '<'){
xml_parser_reset_buffer(thiz);
state = STAT_AFTER_LT;
}
else if(!isspace(c)){
state = STAT_TEXT;
}
break;
}
case STAT_AFTER_LT: {
if(c == '?'){
state = STAT_PROCESS_INSTRUCTION;
}
else if(c == '/'){
state = STAT_END_TAG;
}
else if(c == '!'){
state = STAT_PRE_COMMENT1;
}
else if(isalpha(c) || c == '_'){
state = STAT_START_TAG;
}
else{}
break;
}
case STAT_START_TAG:{
xml_parser_parse_start_tag(thiz);
state = STAT_NONE;
break;
}
case STAT_END_TAG:{
xml_parser_parse_end_tag(thiz);
state = STAT_NONE;
break;
}
case STAT_PROCESS_INSTRUCTION:{
xml_parser_parse_pi(thiz);
state = STAT_NONE;
break;
}
case STAT_TEXT:{
xml_parser_parse_text(thiz);
state = STAT_NONE;
break;
}
case STAT_PRE_COMMENT1:{
if(c == '-'){
state = STAT_PRE_COMMENT2;
}
else{
}
break;
}
case STAT_PRE_COMMENT2:{
if(c == '-'){
state = STAT_COMMENT;
}
else
{
}
}
case STAT_COMMENT:{
xml_parser_parse_comment(thiz);
state = STAT_NONE;
break;
}
default:break;
}
if(*thiz->read_ptr == '\0'){
break;
}
}
return;
}
static void xml_parser_reset_buffer(XmlParser* thiz)
{
thiz->buffer_used = 0;
thiz->attrs_nr = 0;
thiz->attrs[0] = NULL;
return;
}
static int xml_parser_strdup(XmlParser* thiz, const char* start, size_t length)
{
int offset = -1;
if((thiz->buffer_used + length) >= thiz->buffer_total){
size_t length = thiz->buffer_total+(thiz->buffer_total>>1) + 128;
char* buffer = realloc(thiz->buffer, length);
if(buffer != NULL){
thiz->buffer = buffer;
thiz->buffer_total = length;
}
}
if((thiz->buffer_used + length) >= thiz->buffer_total){
return offset;
}
offset = thiz->buffer_used;
strncpy(thiz->buffer + offset, start, length);
thiz->buffer[offset + length] = '\0';
strtrim(thiz->buffer+offset);
thiz->buffer_used += length + 1;
return offset;
}
static void xml_parser_parse_attrs(XmlParser* thiz, char end_char)
{
int i = 0;
enum _State{
STAT_PRE_KEY,
STAT_KEY,
STAT_PRE_VALUE,
STAT_VALUE,
STAT_END,
}state = STAT_PRE_KEY;
char value_end = '\"';
const char* start = thiz->read_ptr;
thiz->attrs_nr = 0;
for(; *thiz->read_ptr != '\0' && thiz->attrs_nr < MAX_ATTR_NR; thiz->read_ptr++){
char c = *thiz->read_ptr;
switch(state){
case STAT_PRE_KEY:{
if(c == end_char || c == '>'){
state = STAT_END;
}
else if(!isspace(c)){
state = STAT_KEY;
start = thiz->read_ptr;
}
}
case STAT_KEY:{
if(c == '='){
thiz->attrs[thiz->attrs_nr++] = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
state = STAT_PRE_VALUE;
}
break;
}
case STAT_PRE_VALUE:{
if(c == '\"' || c == '\''){
state = STAT_VALUE;
value_end = c;
start = thiz->read_ptr + 1;
}
break;
}
case STAT_VALUE:{
if(c == value_end){
thiz->attrs[thiz->attrs_nr++] = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
state = STAT_PRE_KEY;
}
}
default:break;
}
if(state == STAT_END){
break;
}
}
for(i = 0; i < thiz->attrs_nr; i++){
thiz->attrs[i] = thiz->buffer + (size_t)(thiz->attrs[i]);
}
thiz->attrs[thiz->attrs_nr] = NULL;
return;
}
static void xml_parser_parse_start_tag(XmlParser* thiz)
{
enum _State{
STAT_NAME,
STAT_ATTR,
STAT_END,
}state = STAT_NAME;
char* tag_name = NULL;
const char* start = thiz->read_ptr - 1;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
char c = *thiz->read_ptr;
switch(state){
case STAT_NAME:{
if(isspace(c) || c == '>' || c == '/'){
tag_name = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
state = (c != '>' && c != '/') ? STAT_ATTR : STAT_END;
}
break;
}
case STAT_ATTR:{
xml_parser_parse_attrs(thiz, '/');
state = STAT_END;
break;
}
default:break;
}
if(state == STAT_END){
break;
}
}
tag_name = thiz->buffer + (size_t)tag_name;
if(thiz->read_ptr[0] == '/'){
}
for(; *thiz->read_ptr != '>' && *thiz->read_ptr != '\0'; thiz->read_ptr++);
return;
}
static void xml_parser_parse_end_tag(XmlParser* thiz)
{
char* tag_name = NULL;
const char* start = thiz->read_ptr;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
if(*thiz->read_ptr == '>'){
tag_name = thiz->buffer + xml_parser_strdup(thiz, start, thiz->read_ptr-start);
break;
}
}
return;
}
static void xml_parser_parse_comment(XmlParser* thiz)
{
enum _State{
STAT_COMMENT,
STAT_MINUS1,
STAT_MINUS2,
}state = STAT_COMMENT;
const char* start = ++thiz->read_ptr;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
char c = *thiz->read_ptr;
switch(state){
case STAT_COMMENT:{
if(c == '-'){
state = STAT_MINUS1;
}
break;
}
case STAT_MINUS1:{
if(c == '-'){
state = STAT_MINUS2;
}else{
state = STAT_COMMENT;
}
break;
}
case STAT_MINUS2:{
if(c == '>'){
return;
}else{
state = STAT_COMMENT;
}
}
default:break;
}
}
return;
}
static void xml_parser_parse_pi(XmlParser* thiz)
{
enum _State{
STAT_NAME,
STAT_ATTR,
STAT_END
}state = STAT_NAME;
char* tag_name = NULL;
const char* start = thiz->read_ptr;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
char c = *thiz->read_ptr;
switch(state){
case STAT_NAME:{
if(isspace(c) || c == '>'){
tag_name = (char*)xml_parser_strdup(thiz, start, thiz->read_ptr - start);
state = c != '>' ? STAT_ATTR : STAT_END;
}
break;
}
case STAT_ATTR:{
xml_parser_parse_attrs(thiz, '?');
state = STAT_END;
break;
}
default:break;
}
if(state == STAT_END)
{
break;
}
}
tag_name = thiz->buffer + (size_t)tag_name;
for(; *thiz->read_ptr != '>' && *thiz->read_ptr != '\0'; thiz->read_ptr++);
return;
}
static void xml_parser_parse_text(XmlParser* thiz)
{
const char* start = thiz->read_ptr - 1;
for(; *thiz->read_ptr != '\0'; thiz->read_ptr++){
char c = *thiz->read_ptr;
if(c == '<'){
if(thiz->read_ptr > start){
}
thiz->read_ptr--;
return;
}
else if(c == '&'){
xml_parser_parse_entity(thiz);
}
}
return;
}
static void xml_parser_parse_entity(XmlParser* thiz)
{
/*TODO*/
return;
}
void xml_parser_destroy(XmlParser* thiz)
{
if(thiz != NULL){
free(thiz->buffer);
free(thiz);
}
return;
}
static const char* strtrim(char* str)
{
char* p = NULL;
p = str + strlen(str) - 1;
while(p != str && isspace(*p)){
*p = '\0';
p--;
}
p = str;
while(*p != '\0' && isspace(*p)) p++;
if(p != str){
char* s = p;
char* d = str;
while(*s != '\0'){
*d = *s;
d++;
s++;
}
*d = '\0';
}
return str;
}
#ifdef XML_PARSER_TEST
#define XML "<?xml version=\"1.0\" encoding=\"utf-8\"?> \
<!--comment--> <br/> <p>ppp</p> <br />\
<programmer name=\"lixianjing\" blog=\"http://www.limodev.cn/blog\">text</programmer>"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
char* read_file(const char* file_name){
char* buffer = NULL;
FILE* fp = fopen(file_name, "r");
if(fp != NULL){
struct stat st = {0};
if(stat(file_name, &st) == 0){
buffer = malloc(st.st_size + 1);
fread(buffer, st.st_size, 1, fp);
buffer[st.st_size] = '\0';
}
}
return buffer;
}
int main(int argc, char* argv[])
{
XmlParser* thiz = xml_parser_create();
if(argc > 1){
char* buffer = read_file(argv[1]);
xml_parser_parse(thiz, buffer);
free(buffer);
}else{
xml_parser_parse(thiz, XML);
}
xml_parser_destroy(thiz);
return 0;
}
#endif/*XML_PARSER_TEST*/
all:
gcc -g -m32 -Wall count_word.c -DCOUNT_WORD_TEST -o count_word_test
gcc -g -m32 -Wall word_segmentation.c -DWORD_SEGMENTATION_TEST -o word_segmentation_test
gcc -g -m32 -Wall parse_token.c -DPARSE_TOKEN_TEST -o parse_token_test
gcc -g -m32 -Wall ini_parser.c -DINI_PARSER_TEST -o ini_parser_test
gcc -g -m32 -Wall xml_parser.c -DXML_PARSER_TEST -o xml_parser_test
clean:
rm -f *test
10.2 Builder模式¶
/*
* File: typedef.h
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
typedef void (*DataDestroyFunc)(void* ctx, void* data);
typedef int (*DataCompareFunc)(void* ctx, void* data);
typedef Ret (*DataVisitFunc)(void* ctx, void* data);
typedef int (*DataHashFunc)(void* data);
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
typedef Ret (*SortFunc)(void** array, size_t nr, DataCompareFunc cmp);
#define MAX_ATTR_NR 64
#endif/*TYPEDEF_H*/
/*
* File: xml_builder.h
*/
#include "typedef.h"
#ifndef XML_BUILDER_H
#define XML_BUILDER_H
DECLS_BEGIN
struct _XmlBuilder;
typedef struct _XmlBuilder XmlBuilder;
typedef void (*XmlBuilderOnStartElementFunc)(XmlBuilder* thiz, const char* tag, const char** attrs);
typedef void (*XmlBuilderOnEndElementFunc)(XmlBuilder* thiz, const char* tag);
typedef void (*XmlBuilderOnTextFunc)(XmlBuilder* thiz, const char* text, size_t length);
typedef void (*XmlBuilderOnCommentFunc)(XmlBuilder* thiz, const char* text, size_t length);
typedef void (*XmlBuilderOnPiElementFunc)(XmlBuilder* thiz, const char* tag, const char** attrs);
typedef void (*XmlBuilderOnErrorFunc)(XmlBuilder* thiz, int line, int row, const char* message);
typedef void (*XmlBuilderDestroyFunc)(XmlBuilder* thiz);
struct _XmlBuilder
{
XmlBuilderOnStartElementFunc on_start_element;
XmlBuilderOnEndElementFunc on_end_element;
XmlBuilderOnTextFunc on_text;
XmlBuilderOnCommentFunc on_comment;
XmlBuilderOnPiElementFunc on_pi_element;
XmlBuilderOnErrorFunc on_error;
XmlBuilderDestroyFunc destroy;
char priv[1];
};
static inline void xml_builder_on_start_element(XmlBuilder* thiz, const char* tag, const char** attrs)
{
return_if_fail(thiz != NULL && thiz->on_start_element != NULL);
thiz->on_start_element(thiz, tag, attrs);
return;
}
static inline void xml_builder_on_end_element(XmlBuilder* thiz, const char* tag)
{
return_if_fail(thiz != NULL && thiz->on_end_element != NULL);
thiz->on_end_element(thiz, tag);
return;
}
static inline void xml_builder_on_text(XmlBuilder* thiz, const char* text, size_t length)
{
return_if_fail(thiz != NULL && thiz->on_text != NULL);
thiz->on_text(thiz, text, length);
return;
}
static inline void xml_builder_on_comment(XmlBuilder* thiz, const char* text, size_t length)
{
return_if_fail(thiz != NULL);
if(thiz->on_comment != NULL){
thiz->on_comment(thiz, text, length);
}
return;
}
static inline void xml_builder_on_pi_element(XmlBuilder* thiz, const char* tag, const char** attrs)
{
return_if_fail(thiz != NULL);
if(thiz->on_pi_element != NULL){
thiz->on_pi_element(thiz, tag, attrs);
}
return;
}
static inline void xml_builder_on_error(XmlBuilder* thiz, int line, int row, const char* message)
{
return_if_fail(thiz != NULL);
if(thiz->on_error != NULL){
thiz->on_error(thiz, line, row, message);
}
return;
}
static inline void xml_builder_destroy(XmlBuilder* thiz)
{
if(thiz != NULL && thiz->destroy != NULL){
thiz->destroy(thiz);
}
return;
}
DECLS_END
#endif/*XML_BUILDER_H*/
/*
* File: xml_builder_dump.h
*/
#ifndef XML_BUILDER_DUMP_H
#define XML_BUILDER_DUMP_H
#include "xml_builder.h"
DECLS_BEGIN
XmlBuilder* xml_builder_dump_create(FILE* fp);
DECLS_END
#endif/*XML_BUILDER_DUMP_H*/
/*
* File: xml_builder_dump.c
*/
#include "xml_builder_dump.h"
typedef struct _PrivInfo
{
FILE* fp;
}PrivInfo;
static void xml_builder_dump_on_start_element(XmlBuilder* thiz, const char* tag, const char** attrs)
{
int i = 0;
PrivInfo* priv = (PrivInfo*)thiz->priv;
fprintf(priv->fp, "<%s", tag);
for(i = 0; attrs != NULL && attrs[i] != NULL && attrs[i + 1] != NULL; i += 2){
fprintf(priv->fp, " %s=\"%s\"", attrs[i], attrs[i + 1]);
}
fprintf(priv->fp, ">");
return;
}
static void xml_builder_dump_on_end_element(XmlBuilder* thiz, const char* tag)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
fprintf(priv->fp, "</%s>\n", tag);
return;
}
static void xml_builder_dump_on_text(XmlBuilder* thiz, const char* text, size_t length)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
fwrite(text, length, 1, priv->fp);
return;
}
static void xml_builder_dump_on_comment(XmlBuilder* thiz, const char* text, size_t length)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
fprintf(priv->fp, "<!--");
fwrite(text, length, 1, priv->fp);
fprintf(priv->fp, "-->\n");
return;
}
static void xml_builder_dump_on_pi_element(XmlBuilder* thiz, const char* tag, const char** attrs)
{
int i = 0;
PrivInfo* priv = (PrivInfo*)thiz->priv;
fprintf(priv->fp, "<?%s", tag);
for(i = 0; attrs != NULL && attrs[i] != NULL && attrs[i + 1] != NULL; i += 2)
{
fprintf(priv->fp, " %s=\"%s\"", attrs[i], attrs[i + 1]);
}
fprintf(priv->fp, "?>\n");
return;
}
static void xml_builder_dump_on_error(XmlBuilder* thiz, int line, int row, const char* message)
{
fprintf(stderr, "(%d,%d) %s\n", line, row, message);
return;
}
static void xml_builder_dump_destroy(XmlBuilder* thiz)
{
if(thiz != NULL){
free(thiz);
}
return;
}
XmlBuilder* xml_builder_dump_create(FILE* fp)
{
XmlBuilder* thiz = (XmlBuilder*)calloc(1, sizeof(XmlBuilder));
if(thiz != NULL){
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->on_start_element = xml_builder_dump_on_start_element;
thiz->on_end_element = xml_builder_dump_on_end_element;
thiz->on_text = xml_builder_dump_on_text;
thiz->on_comment = xml_builder_dump_on_comment;
thiz->on_pi_element = xml_builder_dump_on_pi_element;
thiz->on_error = xml_builder_dump_on_error;
thiz->destroy = xml_builder_dump_destroy;
priv->fp = fp != NULL ? fp : stdout;
}
return thiz;
}
#ifdef XML_BUILDER_DUMP_TEST
int main(int argc, char* argv[])
{
const char* pi_attrs[] = {"version", "1.0", "encoding", "utf-8", NULL};
const char* root_attrs[] = {"name", "lixianjing", "blog", "http://www.limodev.cn/blog",NULL};
XmlBuilder* thiz = xml_builder_dump_create(stdout);
xml_builder_on_pi_element(thiz, "xml", pi_attrs);
xml_builder_on_comment(thiz,"comment", 6);
xml_builder_on_start_element(thiz, "programmer", root_attrs);
xml_builder_on_text(thiz,"text", 4);
xml_builder_on_end_element(thiz, "programmer");
xml_builder_destroy(thiz);
return 0;
}
#endif/*XML_BUILDER_DUMP_TEST*/
all:
gcc -g -m32 -Wall -DXML_BUILDER_DUMP_TEST xml_builder_dump.c -o xml_builder_dump_test
gcc -g -m32 -Wall -DXML_PARSER_TEST xml_parser.c xml_builder_dump.c xml_builder_tree.c xml_tree.c xml_parser_test.c -o xml_parser_test
gcc -g -m32 -Wall -DXML_TREE_TEST xml_tree.c xml_builder_dump.c -o xml_tree_test
gcc -g -m32 -Wall -DXML_BUILDER_TREE_TEST xml_builder_tree.c xml_tree.c xml_builder_dump.c -o xml_builder_tree_test
clean:
rm -f *test