如何在C语言中将XML文件读入缓冲区?

6
我想用C语言将XML文件读入到char *buffer中。
最佳方法是什么?
我该如何开始?

我认为你在这里的抽象层次混淆了。你特别询问XML文件,但XML文件与任何其他随机访问文件或字节流没有区别。请确保你以正确的抽象层次来解决问题。 - user3458
在C语言中读取文件相当简单。但处理XML标签则是完全不同的问题。 - Anthony Giorgio
2
我不确定为什么这个问题被踩了。这是一个合理的问题。他并没有要求别人把代码摆在盘子里给他(或她),他只是在问如何入门。肯定有一些库和有经验的人可以帮助导入XML文件吧? - Ben
我坚信那些给负评的人应该添加评论,但在这种情况下,可能是因为将文件读入char 缓冲区根本不是特定于XML的。 OP可能想要解析*文件,但用词错误了。 - bortzmeyer
8个回答

12

如果您想要解析XML文件,而不仅仅是将其读入缓冲区(这不是XML特定的操作,详见Christoph和Baget的回答),您可以使用例如libxml2

#include <stdio.h>
#include <string.h>
#include <libxml/parser.h>

int main(int argc, char **argv) {
   xmlDoc *document;
   xmlNode *root, *first_child, *node;
   char *filename;

   if (argc < 2) {
     fprintf(stderr, "Usage: %s filename.xml\n", argv[0]);
     return 1;
   }
   filename = argv[1];

  document = xmlReadFile(filename, NULL, 0);
  root = xmlDocGetRootElement(document);
  fprintf(stdout, "Root is <%s> (%i)\n", root->name, root->type);
  first_child = root->children;
  for (node = first_child; node; node = node->next) {
     fprintf(stdout, "\t Child is <%s> (%i)\n", node->name, node->type);
  }
  fprintf(stdout, "...\n");
  return 0;
}

在Unix机器上,您通常使用以下命令编译上述内容:
% gcc -o read-xml $(xml2-config --cflags) -Wall $(xml2-config --libs) read-xml.c

1.0E99 谢谢!代码 和编译标志 是一个很好的例子。真不敢相信8年来没有人将其标记为有用? - phs

7

将文件内容读入单个简单缓冲区中,这真的是您想要做的吗?XML文件通常需要被解析,您可以使用像libxml2这样的库来完成,只是举一个例子(但值得注意的是,它是用C实现的)。


4

希望没有bug的ISO-C代码来读取文件内容并添加'\0'字符:

#include <stdlib.h>
#include <stdio.h>

long fsize(FILE * file)
{
    if(fseek(file, 0, SEEK_END))
        return -1;

    long size = ftell(file);
    if(size < 0)
        return -1;

    if(fseek(file, 0, SEEK_SET))
        return -1;

    return size;
}

size_t fget_contents(char ** str, const char * name, _Bool * error)
{
    FILE * file = NULL;
    size_t read = 0;
    *str = NULL;
    if(error) *error = 1;

    do
    {
        file = fopen(name, "rb");
        if(!file) break;

        long size = fsize(file);
        if(size < 0) break;

        if(error) *error = 0;

        *str = malloc((size_t)size + 1);
        if(!*str) break;

        read = fread(*str, 1, (size_t)size, file);
        (*str)[read] = 0;
        *str = realloc(*str, read + 1);

        if(error) *error = (size != (long)read);
    }
    while(0);

    if(file) fclose(file);
    return read;
}

3
  1. 在Visual Studio中安装libxml2 NuGet包(我使用Vs 2015测试)
  2. 复制并粘贴示例XML文件中的内容到记事本中,并将文件保存为example.xml
  3. 将//xml解析下的代码复制并粘贴到Vs中
  4. 从主函数中调用带有xml文件名作为参数的函数
  5. 你将会在configReceive中得到xml数据

就这些...

示例XML文件:

<?xml version="1.0" encoding="utf-8"?>
    <config>
        <xmlConfig value1="This is a simple XML parsing program in C"/>
        <xmlConfig value2="Thank you : Banamali Mishra"/>
        <xmlConfig value3="2000000"/>
        <xmlConfig value4="80"/>
        <xmlConfig value5="10"/>
        <xmlConfig value6="1"/>
    </config>

这里是源代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

char configReceive[6][80] = { " " };

//xml parsing
void ParsingXMLFile(char *filename) {
    char         *docname;
    xmlDocPtr    doc;
    xmlNodePtr   cur;
    xmlChar      *uri;
    char config[6][80] = { "value1", "value2", "value3", "value4", "value5", "value6" };
    int count = 0;
    int count1 = 0;

    docname = filename;
    doc = xmlParseFile(docname);
    cur = xmlDocGetRootElement(doc);
    cur = cur->xmlChildrenNode;
    while (cur != NULL) {
        if ((!xmlStrcmp(cur->name, (const xmlChar *)"xmlConfig"))) {
            uri = xmlGetProp(cur, (xmlChar *)config[count++]);
            strcpy(configReceive[count1++], (char *)uri);
            xmlFree(uri);
        }
        cur = cur->next;
    }

    count = 0;
    count1 = 0;
    xmlFreeDoc(doc);
}

这是可运行的代码。如果有意见,请提供您的评论。谢谢。 - Banamali Mishra

2
你可以使用stat()函数获取文件大小,然后在使用fread读取文件后使用malloc分配缓冲区。
代码应该如下所示:
struct stat file_status;
char *buf = NULL;
FILE * pFile;

stat("tmp.xml", &file_status);
buf = (char*)malloc(file_status.st_size);
pFile = fopen ("tmp.xml","r");
fread (buf,1,file_status.st_size,pFile);

fclose(pFile);

你不应该对malloc()进行强制类型转换,并且应该检查其返回值。此外,stat()不是标准的(我指的是ISO C标准)。 - Nietzche-jou
@sgm:修改代码并发布,附上您的更改以及为什么进行更改。我很乐意投票支持它。 - GEOCHET
答案是正确的,但可能不是 OP 想要的 :-) 他可能想要 解析 XML 文件。 - bortzmeyer
@sgm:在C90中,强制类型转换malloc是不正确的。你应该将其转换为与C++兼容,并且在C99中没有危险,因为隐式函数声明不再合法。 - Adam Rosenfield

1

这里有一个完整的程序,可以将整个XML文件(实际上是任何文件)读入缓冲区。它包括了足够有用的错误检查。

注意:所有操作都在main()中完成。将其转换为可调用函数留给读者作为练习。

(已测试,使用GCC 4.3.3编译。开关为-Wall -W --pedantic --ansi。)

对此的评论将在大约八小时后得到回复。

#include <stdio.h>
#include <stdlib.h>


int main (int argc, char *argv[]) {
 char   *buffer;        /* holds the file contents. */
 size_t  i;             /* indexing into buffer. */
 size_t  buffer_size;   /* size of the buffer. */
 char   *temp;          /* for realloc(). */
 char    c;             /* for reading from the input. */
 FILE   *input;         /* our input stream. */


 if (argc == 1) {
      fprintf(stderr, "Needs a filename argument.\n");
      exit(EXIT_FAILURE);
 }
 else if (argc > 2) {
      fprintf(stderr, "Well, you passed in a few filenames, but I'm only using %s\n", argv[1]);
 }

 if ((input = fopen(argv[1], "r")) == NULL) {
      fprintf(stderr, "Error opening input file %s\n", argv[1]);
      exit(EXIT_FAILURE);
 }

 /* Initial allocation of buffer */
 i = 0;
 buffer_size = BUFSIZ;
 if ((buffer = malloc(buffer_size)) == NULL) {
      fprintf(stderr, "Error allocating memory (before reading file).\n");
      fclose(input);
 }

 while ((c = fgetc(input)) != EOF) {
      /* Enlarge buffer if necessary. */
      if (i == buffer_size) {
       buffer_size += BUFSIZ;
       if ((temp = realloc(buffer, buffer_size)) == NULL) {
        fprintf(stderr, "Ran out of core while reading file.\n");
        fclose(input);
        free(buffer);
        exit(EXIT_FAILURE);
       }
       buffer = temp;
      }

      /* Add input char to the buffer. */
      buffer[i++] = c;
 }

 /* Test if loop terminated from error. */
 if (ferror(input)) {
      fprintf(stderr, "There was a file input error.\n");
      free(buffer);
      fclose(input);
      exit(EXIT_FAILURE);
 }

 /* Make the buffer a bona-fide string. */
 if (i == buffer_size) {
      buffer_size += 1;
      if ((temp = realloc(buffer, buffer_size)) == NULL) {
       fprintf(stderr, "Ran out of core (and only needed one more byte too ;_;).\n");
       fclose(input);
       free(buffer);
       exit(EXIT_FAILURE);
      }
      buffer = temp;
 }
 buffer[i] = '\0';

 puts(buffer);

 /* Clean up. */
 free(buffer);
 fclose(input);

 return 0;
}

0

我认为那个问题是关于XML解析而不是文件读取的,然而OP应该真正澄清这一点。
无论如何,你有很多读取文件的例子。
除了sgm建议的xml解析之外,另一个选择是Expat库


0

建议:使用内存映射

这有可能减少数据无用复制的情况。诀窍是向操作系统请求所需内容,而不是自己处理。以下是我之前实现的一个例子:

mmap.h

#ifndef MMAP_H
#define MMAP_H

#include <sys/types.h>

struct region_t {
  void *head;
  off_t size;
};

#define OUT_OF_BOUNDS(reg, p) \
  (((void *)(p) < (reg)->head) || ((void *)(p) >= ((reg)->head)+(reg)->size))

#define REG_SHOW(reg) \
  printf("h: %p, s: %ld (e: %p)\n", reg->head, reg->size, reg->head+reg->size);

struct region_t *do_mmap(const char *fn);
#endif

mmap.c

#include <stdlib.h>

#include <sys/types.h>  /* open lseek             */
#include <sys/stat.h>   /* open                   */
#include <fcntl.h>      /* open                   */
#include <unistd.h>     /*      lseek             */
#include <sys/mman.h>   /*            mmap        */

#include "mmap.h"

struct region_t *do_mmap(const char *fn)
{
  struct region_t *R = calloc(1, sizeof(struct region_t));

  if(R != NULL) {
    int fd;

    fd = open(fn, O_RDONLY);
    if(fd != -1) {
      R->size = lseek(fd, 0, SEEK_END);
      if(R->size != -1) {
        R->head = mmap(NULL, R->size, PROT_READ, MAP_PRIVATE, fd, 0);
        if(R->head) {
          close(fd); /* don't need file-destructor anymore. */
          return R;
        }
        /*                no clean up of borked (mmap,) */
      }
      close(fd);   /* clean up of borked (lseek, mmap,) */
    }
    free(R); /* clean up of borked (open, lseek, mmap,) */
  }
  return NULL;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接