如何在C中解析文件

假设条件

我假设您知道或能够查找我在此处描述的功能,并对C编程有一定的了解。

费耶

尽管我将本文称为“如何使用C ++解析文件”,但实际上我们主要是对文件进行词法化,即将流分解成其组成部分,而不考虑流包含的语法。 解析实际上包括语法以使其有意义。

认为词汇化是一堆单词中的阅读,而解析则是一句话中的阅读。 每个单词都具有某种含义,但是如果没有句子的上下文,就意味着没有任何非常有用的含义。

我没有使用“如何在C ++中进行词法分析”这一标题,因为你们中的大多数人可能都不知道这意味着什么。 如果您这样做,那么我道歉。

介绍

如何解析文件的问题在TSDN上经常出现,所以我决定就此问题编写一些东西,以帮助所有人,而不必一遍又一遍地重复自己的工作。 在使用这些C函数时,我也会解决安全问题,因为如果滥用它们,可能会造成危险。

我将首先定义一些术语,以使您对背景有所了解。

流和文件

对于那些不熟悉C的人,我现在要声明它可以将许多东西视为一个文件,包括键盘(又名终端输入或标准输入)和显示(终端输出或标准输出)。 还有第三个输出,称为标准错误,用于输出错误消息,默认情况下输出到终端(本段是您最后一次在本文中直接讨论标准错误)。 这三个是通过对象stdin,stdout和stderr进行的访问。

这三个文件(或流)在C标准委员会定义的任何终端应用程序启动时都将打开,并且可以执行常规文件可以执行的任何操作,但只能查找文件中的任意位置。 因此,IO例程通常应用于流而不是文件(文件是流的专业化或更具体的示例)。

要理解这一点,请考虑一下水槽水龙头流出的水(即一股水)。 用手指伸入其中。 考虑到您用手指正在“读取”流过它的所有水分子。 但是,一旦过去,您将无法在流中移动手指并重新阅读刚刚阅读的内容。 那些水分子已经流到了您无法企及的范围。 您也不能将手指向后移过水龙头的嘴,您必须耐心等待“数据”到来。

文件实际上是更专业的。 它允许您在文件中四处寻找,这意味着您可以指向文件中的任何位置以从中读取或写入文件。 这样的文件称为随机访问文件,因为您可以按任意顺序读取它。

缓冲和双缓冲

双缓冲意味着在处理/显示之前从一个缓冲区转储到另一个缓冲区。

缓冲功能

stdio.h具有几个以f开头的文件功能。 fopen(),fread(),fwrite(),fflush(),fscanf(),fprintf(),fgetc(),fputc(),fseek()和fclose()

[*] 。 所有这些功能都已缓冲,这意味着如果要读取一个字节然后读取第二个字节,则只会发生一次磁盘读取。

使用这些功能需要使用FILE。 FILE是一个结构,其中包含执行所需操作所需的所有“材料”。

发生这种情况是因为在第一次读取时,不仅读取了一个字节,而且读取了一大块数据并将其存储在缓冲区中。 该缓冲区位于由stdio库分配的内存中的某个位置。 然后它将数据复制到您指定的数据空间。 当请求下一个字节时,stdio库不必从驱动器请求数据,因为它已经获取了一大块数据。 这样可以大大加快阅读速度。

写作是相似的。 除非您填满缓冲区,否则显式刷新缓冲区;对于文本文件,除非输出'\ n'字符,否则数据将不会发送到文件。

当读取字节或数据字符串时,实际上是双重缓冲。 这是因为它首先被读取到内部stdio缓冲区中,然后再复制到您的缓冲区中。

当读入数字时,它仅是单缓冲的,因为它被读入内部stdio缓冲区,在那里进行处理,并且

然后将写入您指定的存储位置。 [*] scanf()和printf()是方便的函数,它们分别使用FILE的stdin和stdout,而无需告知。 getchar()和putchar()也使用stdin并可以分别替换fgetc()和fputc()。 非缓冲功能

还有一些文件函数不带f前缀。 即,open(),read(),write(),lseek()和close()。 请注意,没有冲洗功能。 这是因为这是一个低级调用,并且与这些函数关联的唯一缓冲区是程序员提供的缓冲区。 应该知道,并非总是如此。 它可以取决于文件系统的实现,因此受操作系统控制。 同样,fscanf()和fprintf()也不可用,因为这是一个非常简单的接口。

这些函数与一个称为文件描述符的int相关联。 它跟踪执行文件所需的所有“材料”,就像FILE一样。 但是,跟踪通常是由操作系统而不是应用程序完成的。

如前所述,缓冲与实现有关。 终端服务(在符合POSIX的系统下)被缓冲,要刷新它们,需要调用ioctl()并传递文件描述符并使用特定的控制代码(TCIFLUSH用于丢弃缓冲的输入,TCOFLUSH用于刷新输出,而TCIOFLUSH用于两者)。

磁盘IO可以变为双缓冲。 这是因为某些操作系统不允许一次读取少于一个扇区的任何内容。 因此,为此,库可以将扇区写入内部缓冲区,复制所请求的内容,然后丢弃其余部分。 这将大大降低代码的运行速度,因为将不会跟踪已经读取的数据,并且如果在接下来的几个字节中读取,它将再次重新读取该扇区。

可以使用fdopen()命令将文件描述符制成FILE。 当您打开管道时,这可能非常有用。

解析文件

使用描述的缓冲技术可以非常简单地完成文件解析。

我不会使用文件描述符。 从这里开始,我将只使用FILE流。 这是因为它速度更快,并且具有原始文件描述符未提供的许多功能。

没有双缓冲的解析

在没有双缓冲的情况下解析文件并不总是可能的。 唯一的方法是只读取和存储数字。

例如,这是一个示例文件:


1, 2, 3, 4, 5
6, 7, 8, 9, 10 
要在没有双重缓冲的情况下读入,可以循环以下操作:

/* CODE FRAGMENT 1 */
int itemsParsed = 0;
int items[5];
itemsParsed =
scanf("%d, %d, %d, %d, %d", &items[0], &items[1], &items[2], &items[3],
      &items[4]); 
请注意,输入流中必须使用逗号。 但是,空格表示0或多个空格。 空格可以是常规空格,制表符,垂直制表符(很少使用),回车符或换行符。

还要注意,scanf()返回已解析的项目数

那不是文字 。 即逗号和空格将被忽略。 您应该查看此值以确保已收到所有期望的物品。

/* CODE 1 */
#include <stdio.h>
int main()
{
    int itemsParsed = 0;
    int items[5]; 
    memset(items, 0, sizeof(items));    /* Set all items to zero */ 
    /* Read in 5 comma separated values */
    itemsParsed =
        scanf("%d, %d, %d, %d, %d", &items[0], &items[1], &items[2],
              &items[3], &items[4]);
    printf("itemsParsed = %d\n", itemsParsed);
    printf("%d, %d, %d, %d, %d\n", items[0], items[1], items[2], items[3],
           items[4]); 
    memset(items, 0, sizeof(items));    /* Set all items to zero */ 
    /* Read in 4 comma separated values with first by with a comma */
    itemsParsed =
        scanf(", %d, %d, %d, %d", &items[0], &items[1], &items[2],
              &items[3]);
    printf("itemsParsed = %d\n", itemsParsed);
    printf("%d, %d, %d, %d\n", items[0], items[1], items[2], items[3]);
    return 0;
} 
现在,CODE 1的操作是读取5个逗号分隔的值,然后尝试读取另外4个逗号分隔的值的逗号。

试试看。 如果在剪切和粘贴时遇到问题,它也以CODE1.zip的形式附加到本文档的末尾。 如果您输入

1,2,3,4将读取4个数字,如果您键入1,2,,3,4,5 ,它将读取2个数字,然后读取3个数字。

现在尝试

1,2,3,4,5 。 发生了什么? 它不允许您输入更多数据,只是说它没有为第二个scanf()调用解析任何内容。 为什么? 因为要读的下一个字符是逗号。 如果要确保在文字字符之前读取所有空白,则必须在其前面加上一个空格。

但是,如果您想跳过所有空格怎么办

除了回车或换行? 为此,您需要使用一个字符类。 字符类是一个非常简化的正则表达式。 它将读取该类指定的一个或多个字符。 例如:

/* CODE FRAGMENT 2 */
(void)scanf(“%*[ \t\v]”); 
将读入并丢弃所有空格,制表符或垂直制表符。 我不存储已解析项目的数量,因为我不仅不在乎它是否已读取任何内容,而且已解析元素的数量中不包含加星标('*')参数,因此它不会告诉我任何内容无论如何。 仅存储到某个位置的参数包含在已解析元素的返回值中。

如果我想知道我是否在任何空格,制表符或垂直制表符中进行过读取,可以使用“%n”说明符,该说明符将说明从格式字符串的开头到遇到“%”时已读取了多少个字符。 n”说明符。 例如:


/* CODE FRAGMENT 3 */
int bytesRead=0;
(void)scanf(“%*[ \t\v]%n”, &bytesRead); 
注意:我已将bytesRead初始化为零。 如果没有读取空格,制表符或垂直制表符,则不会更新bytesRead,因此其值将不确定。

我也将返回值强制转换为(void)。 这是因为某些编译器会警告我忽略了返回值。 这是因为不应忽略返回值,但是在这种情况下可以这样做。 强制转换为void会告诉编译器:“是的,我知道我忽略了返回值,它是合法的。”

使用双缓冲进行解析

在上一节中,我展示了如何不对数据进行双重缓冲。 但是,有时候这是不可能的。

读取字符串或一系列字符本质上要求使用双缓冲。 数据被读取到内部stdio缓冲区中,然后复制到程序的数据空间中。 然后,可以通过进一步处理或显示它来以任何希望的方式对其进行处理。

要读入以空格分隔的字符串,可以使用scanf()的格式说明符“%

<bufferSize-1> s”,其中bufferSize是您要写入的缓冲区的大小。 未指定bufferSize的情况下, 切勿单独使用“%s”,因为这将导致缓冲区溢出错误,从而使程序不安全并导致难以发现错误。 为确保缓冲区以NULL('\ 0')结尾,以便可以将其用作c字符串,应始终将缓冲区中的最后一个元素设置为'\ 0'。

要使用除空格之外或除空格之外的定界符读取字符串,请使用否定的字符类。 “%

<bufferSize-1> [ ^ :]”表示它将读取一个由bufferSize-1个字节组成的字符串,该字符串由冒号(':')字符组成。 在此处使用与我在“%s”说明符中所述相同的预防措施。 使用三重缓冲进行解析

是的,您可以缓冲该缓冲区的缓冲区。 你为什么想做这个? 我能想到的一个原因是将部分代码与流分离。 这使您可以轻松地将其传递给字符串,并且可以简化调试。

要读一行,可以使用一个称为getline()的非标准函数,该函数在Web上可用,也可以作为gcc扩展名使用。 它将自动分配您需要的内存。 您也可以使用也是gcc扩展的getdelim()。 它与getline()非常相似,但是您可以选择一个定界符。

从那里,您可以使用sscanf()来解析字符串,就像我在scanf()中描述的一样。 使用此三重缓冲技术时,也可以使用atoi(),atol(),atof(),strtol(),strtoul(),strtod(),strtoll(),strtok()和其他c字符串操纵器。

二进制文件

到目前为止,我还没有说过有关二进制文件的任何信息。 这是由于字节序不兼容。 字节顺序是指存储的二进制表示形式的字节顺序。 在Motorola CPU上,它通常存储在big-endian中,Intel使用little-endian,而网络协议通常使用big-endian。 如果要编写一个将数据存储到磁盘或通过网络传递数据的程序,以便可以使用不同的字节序方案从另一台计算机读取该数据,则需要了解此问题以及如何进行纠正。

这并不难,但也不是不重要的。 您需要做的是确保在读取和写入端都使用通用方案。 这可以通过使用宏或函数来完成,这些宏或函数将在写入字节之前反转字节(如果需要),并在读取字节之后但在处理它们之前再次反转字节(如果需要)。

这超出了本文档的范围。 如果有足够的兴趣,我将在另一个文档中进行介绍。

结论

解析文件不是很困难,如果采取适当的预防措施,则可以安全地使用scanf()及其关联文件。

如果您有任何疑问或不清楚的地方。 随时发布消息,我会尽快与您联系和/或更新文档。

阿德里安

修订历史记录 20/05/2007 13:00
  • 纠正语法错误
20/05/2007 13:45
  • 更正了错误,知道文件是什么
29/05/2007 12:44
  • 在文档开始处添加了仅供参考

本文档受知识共享保护

知识共享署名-非商业性共享相同3.0许可
附加的文件
文件类型:zip CODE1.zip (392字节,1019视图)

From: https://bytes.com/topic/c/insights/657086-how-parse-file-c

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在CODESYS解析XML文件,需要使用XML库函数。以下是解析XML文件的基本步骤: 1. 在CODESYS创建一个新的PLC程序,然后导入XML库函数。 2. 在PLC程序定义一个字符串变量,用于存储XML文件的内容。 3. 使用文件读取函数,将XML文件的内容读取到字符串变量。 4. 使用XML库函数,解析XML文件。可以使用XMLRead函数来读取XML文件的元素和属性。 5. 将解析后的XML数据存储在PLC的变量,以便后续使用。 下面是一个简单的示例程序,演示了如何在CODESYS解析XML文件: ``` PROGRAM ParseXML VAR XMLString : STRING; XMLDoc : XML_DOM_Document; XMLRoot : XML_DOM_Element; XMLNode : XML_DOM_Node; END_VAR // Read XML file into string variable XMLString := File.ReadText('C:\example.xml'); // Parse XML string XMLDoc := XML_DOM_Parse(XMLString); IF XMLDoc <> 0 THEN // Get root element XMLRoot := XML_DOM_GetDocumentElement(XMLDoc); // Loop through child nodes FOR XMLNode IN XML_DOM_GetChildNodes(XMLRoot) DO // Check if node is an element IF XML_DOM_NodeType(XMLNode) = XML_DOM_NodeType_Element THEN // Get element name ELEMENT_NAME := XML_DOM_GetNodeName(XMLNode); // Get element value ELEMENT_VALUE := XML_DOM_GetNodeValue(XMLNode); END_IF END_FOR // Free XML document XML_DOM_FreeDocument(XMLDoc); ELSE // Error parsing XML END_IF ``` 请注意,上述示例程序仅演示了解析XML文件的基本方法。实际应用,您可能需要使用更复杂的XML结构和更多的XML库函数来解析XML文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值