【问题标题】:Issue when using sscanf使用 sscanf 时的问题
【发布时间】:2021-06-03 04:01:25
【问题描述】:

我正在尝试用 C 语言编写一个脚本,它从文件 /proc/net/dev 中读取带宽信息并对其进行处理以产生每秒上传和下载流量。

文件如下所示:

Inter-|   Receive                                                |  Transmit
 face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed
    lo:    4845      60    0    0    0     0          0         0     4845      60    0    0    0     0       0          0
enp3s0: 197557966  217836    0    0    0     0          0       591 21516707  160167    0    0    0     0       0          0
  sit0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0

我在两个网络之间切换(另一个网络当前未在文件中列出),因此我将网络设备的名称作为参数。 目前enp3s0 是我从中获取网络信息的设备。

现在我遇到的问题是当我尝试使用 sscanf 处理所需的行时。 出于某种原因,sscanf 总是为我从中读取的各种值生成乱码输出。

所以我制作了一个单独的测试文件,在其中我直接将这一行声明为一个字符串,并使用几乎完全相同的逻辑,它可以完美地按预期工作。

主文件:

注意:在故障排除过程中,某些行已被注释掉。 有些行仅用于详细输出。 另外字节转换部分还是留下来了,因为遇到这个问题,我没有再进一步。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define NETWORK_FILE "/proc/net/dev"

//==========================FUNC_DECL
unsigned long * receive(FILE *,char*);
char *searchstr(FILE *,char *);

//===========================TYPE-STRING
typedef char *String;

//===========================FUNCTIONS
// The function that looks for argv[1] in the NETWORK_FILE and returns the line as char *.
char *searchstr(FILE *ndev,char *netid){
           int found=0,lineno=1;
           char tmpf[600];
           //GETTING THE LINE MATCH
           while(fgets(tmpf,sizeof(tmpf),ndev) != NULL){
                 if(strstr(tmpf,netid) != NULL) {
                            printf("Device %s Found on Line %d",netid,lineno);
                            found++;
                            break;
                 }
                 lineno++;
           }
           if (found==0){ printf("ERROR!::NO_NET_DEVICE::No such device exists as %s\n",netid); }

           char *line = tmpf;
           if(line == NULL){ printf("ERROR!:SEARCHSTR_NULL_LINE_RET: Empty Line being sent for parsing"); exit(2);}
           return line;
}
//The function that sends Upload and Download bytes to the Main function in an array ptr form.. sort of
unsigned long * receive(FILE *dev,char *netname) {
           unsigned int dspeed=0,uspeed=0,dump;
           unsigned long rcv1,rcv2,trv1,trv2;
           char *n_line = (char *)malloc(200*sizeof(char));
           char *drop =(char *) malloc(15*sizeof(char));

           n_line = searchstr(dev,netname); 
           if(n_line == NULL){ printf("Error!: No Line received for parsing"); }
           printf("\n Line Input is: \n%s",n_line);
//--------THIS IS THE MAIN LINE WHERE THE PROBLEM IS. THE LINE BELOW IT IS THE ONE I WROTE TO TROUBLESHOOT
           /*sscanf(n_line,"%s %lu  %u    %u    %u    %u     %u          %u       %u %lu  %u    %u    %u    %u     %u       %u          %u",drop,&rcv1,&dump,&dump,&dump,&dump,&dump,&dump,&dump,&trv1,&dump,&dump,&dump,&dump,&dump,&dump,&dump);*/
           sscanf(n_line,"%s%lu",drop,&rcv1);
           printf("\nDrop is read as: \t %s\nRx is %lu",drop,rcv1);
           if(rcv1 == 0){ printf("\nError!:NULL_VALUE-RX-1: Value received for parsing is 0."); }
           sleep(1);
           sscanf(n_line,"%s %lu  %u    %u    %u    %u     %u          %u       %u %lu  %u    %u    %u    %u     %u       %u          %u", drop,&rcv2,&dump,&dump,&dump,&dump,&dump,&dump,&dump,&trv2,&dump,&dump,&dump,&dump,&dump,&dump,&dump);
           if(rcv2 == 0){ printf("Error!:NULL_VALUE-RX-2: Value received for parsing is 0."); }
           dspeed = rcv2-rcv1;
           uspeed = trv2-trv1;
           unsigned long *speeds [2];
           speeds[0] = &dspeed;
           speeds[1] = &uspeed;
           return *speeds;
}


//==========================Main
int main(int argc,char *argv[]) {

        FILE *netdev;
        String networkid = malloc(10);
        networkid=argv[1];
        netdev = fopen(NETWORK_FILE,"r");
        if (netdev == NULL) { printf("Error Opening file!"); return (-1); }
        unsigned long *spd[2];
        *spd = receive(netdev,networkid);
        printf("\n D: %lu | U: %lu \n",*spd[0],*spd[1]);
        fclose(netdev);
}


测试文件

注意:测试文件的值稍旧,因为它使用的是旧的复制粘贴。

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

int main(int argc,char *argv[]) {
   char *w1,*w2,*w3,*w4,*w5;
   w1 = (char*) malloc(8*sizeof(char));
   w2 = (char*) malloc(8*sizeof(char));
   w3 = (char*) malloc(8*sizeof(char));
   w4 = (char*) malloc(8*sizeof(char));
   w5 = (char*) malloc(15*sizeof(char));
   unsigned long n1,n2,d1,d2,d3,d4,d5,d6,d7;
   char *teststring,*devstring;
   devstring = (char *) malloc(150*sizeof(char));
   devstring = "enp3s0: 168010376  192508    0    0    0     0          0       547 19528703  142230    0    0    0     0       0          0";
   sscanf(devstring,"%s %lu",w5,&d1);
   printf("\n\nDev line parse is:\n %s RX: %lu\n",w5,d1);
}

输出

主文件

Device enp3s0 Found on Line 4
 Line Input is:
enp3s0: 197678794  218123    0    0    0     0          0       617 21694353  160864    0    0    0     0       0          0


Drop is read as:                   <= There is usually gibberish here. Its different everytime. And sometimes its not there at all
Rx is 94544931811891
 D: 94544931811891

测试文件

Dev line parse is:
 enp3s0: RX: 168010376

我几乎是 C 的初学者(这是我第一个使用指针的程序。我仍然无法理解这些)所以肯定会以可以想象的最糟糕/愚蠢的方式做一些事情。请随时纠正我。

编辑:

所以根据@Mathieu 的回答,我尝试为 sscanf 合并退货检查:

           int assign_count = sscanf(n_line,"%s %lu %*u %*u %*u %*u %*u %*u %*u %lu %*u %*u %*u %*u %*u %*u %*u",drop,&rcv1,&trv1);
           if(assign_count == 3){
               printf("\nRx is %lu",rcv1); 
           }else { printf("\nERROR!::RECEIVE_ASSIGN_FAILURE:: Failed to assign %d values(Current: %d)",3,assign_count); }

输出: 这会随机给我两个不同的输出。

Device enp3s0 Found on Line 4
 Line Input is:
enp3s0: 201330925  226694    0    0    0     0          0       790 23566091  172240    0    0    0     0       0          0

ERROR!::RECEIVE_ASSIGN_FAILURE:: Failed to assign 3 values(Current: -1)
 D: 93951710999123
Device enp3s0 Found on Line 4
 Line Input is:
enp3s0: 201339561  226756    0    0    0     0          0       798 23586994  172381    0    0    0     0       0          0

ERROR!::RECEIVE_ASSIGN_FAILURE:: Failed to assign 3 values(Current: 2)
Error!:NULL_VALUE-RX-1: Value received for parsing is 0.Error!:NULL_VALUE-RX-2: Value received for parsing is 0.
 D: 0

在一种情况下,retassign_count 显示为 -1,另一种情况下显示为 2。在某些执行过程中,我也随机遇到 Segmentation Fault

【问题讨论】:

  • 你应该检查sscanf返回的值,它会告诉你已经进行了多少转换,那么你可以读取哪些字段
  • 如果您需要忽略一次转换,请使用%*d,而不是存储在dump
  • 等等我不这样做吗?输出Drop is read as:Rx is 中的行是我用来检查sscanf 提供的值的。或者您是说 sscanf 有一种方法可以实际显示我的输入字符串的默认格式?
  • char *line =(char *) malloc(sizeof(tmpf)); line = tmpf; 毫无意义。您分配空间,然后丢弃对它的唯一引用。
  • line = tmpf之后,line持有一个地址,该地址在函数返回后无效。您需要 strcpy(或类似名称)而不是作业。

标签: c linux pointers scanf


【解决方案1】:

好的,经过进一步的故障排除后,我发现n_line 已损坏。它作为一个整体正确打印,但是当我尝试解析它时,它有一些问题。

首先我通过使用strtokfor 循环打印拆分字符串来分析searchstr 函数中的tmpf 变量。 - 它按预期正确打印

char *wd;
wd = strtok(tmpf," ");
while( wd != NULL){
   printf("::%s\n",wd);
   wd = strtok(NULL," ");
}

但是当我在 n_line 上的 receive 函数中运行相同的函数时,它会产生损坏的输出 - 打印第一个字符串或从行中随机打印一些数字。

解决方案:..sort of

但是,当我将 tmpf 声明为 static 时,整个问题就会一时兴起。

当声明为 static 时,sscanf 正确写入所有变量,并且详细输出也正确输出。

Device enp3s0 Found on Line 4
tmpf is : enp3s0: 4054772    8380    0    0    0     0          0       131  1842262   11148    0    0    0     0       0          0

 Line Input is:
enp3s0: 4054772    8380    0    0    0     0          0       131  1842262   11148    0    0    0     0       0          0
        Rx is 4054772 | Tx is 1842262

我真的不知道它为什么突然工作,但我的猜测是 static 使 tmpf 在两个函数中都留在内存中,因此我能够在第二个函数中正确处理它的输出。仍然感觉这更像是一个hack,而不是一个实际的解决方案。 其他一些输出也会损坏,但这可能不是这个问题的一部分。

编辑:

我还刚刚找到了一个说明这一点的消息来源: https://www.tutorialspoint.com/cprogramming/c_return_arrays_from_function.htm

【讨论】:

    【解决方案2】:

    您对sscanf 的使用可能会更好:

    • 在使用结果之前读取sscanf 的结果
    • 要知道 scanf 将忽略要读取的字段之间的额外空间,您只能在 format 参数中写入一个
    • 要求sscanf 忽略带有%*.. 的某些字段

    因此,您的代码将变为:

    #include <stdio.h>
    
    int main(void) {
    
        char *lines[] = {
            "    lo:    4845      60    0    0    0     0          0         0     4845      60    0    0    0     0       0          0",
            "enp3s0: 197557966  217836    0    0    0     0          0       591 21516707  160167    0    0    0     0       0          0",
            "  sit0:       0       0    0    0    0     0          0         0        0       0    0    0    0     0       0          0"
        };
    
        long unsigned int rcv2, trv2, i;
        char name[256];
    
        for (i = 0; i < 3; ++i)
        {
            int ret = sscanf(lines[i],"%s %lu %*u %*u %*u %*u %*u %*u %*u %lu %*u %*u %*u %*u %*u %*u %*u", name,&rcv2,&trv2);
            if (3 == ret)
                printf("%s, %lu, %lu\n", name, rcv2, trv2);
            else
                printf("Error: %d conversion(s) instead of 3\n", ret);
        }
    }
    

    【讨论】:

    • 好的,所以 %* 确实是很棒的建议。我确实一直在寻找类似的东西。现在我尝试在我的代码中合并int ret = 逻辑,似乎 sscanf 无法分配所有 3 个变量。它在每次执行时将其值从 -1 更改为 2。我将更新我的问题以包含此输出。
    猜你喜欢
    • 2018-04-25
    • 1970-01-01
    • 1970-01-01
    • 2017-06-03
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 2014-04-15
    相关资源
    最近更新 更多