使用 sscanf 时的问题

Issue when using sscanf

我正在尝试用 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 语言的初学者(这是我第一个使用指针的程序。我仍然无法理解这些)所以肯定会以 worst/dumb 可以想象的方式完成一些事情.欢迎指正。

编辑:

所以根据@Mathieu 的回答,我尝试合并一个 return 检查 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 的使用可能会更好:

  • 在使用其结果
  • 之前先阅读sscanf的结果
  • 知道 scanf 会忽略要读取的字段之间的额外 space,您可以在 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);
    }
}

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

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

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

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

解决方案:..有点

然而,当我将 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