alternative for sscanf string with spaces in c?

瘦欲@ 提交于 2020-04-17 19:10:12

问题


I am trying to retrieve information from /proc/cpuinfo file. I have retrieved number of cpu core using sscanf.

Now I am trying to retrieve model name similarly but I sscanf is not working this time because model name is a string including spaces.

Is there any alternative to retrieve it?

char *get_cpu_model()
{
   int fp;
   int r;
   char* match;
   char *cpu_model;

   /* Read the entire contents of /proc/cpuinfo into the buffer. */
   fp = open("/proc/cpuinfo",O_RDONLY);

    if (fp == -1) 
   {   
       printf("Error! Could not open file\n"); 
       return 0;
   } 
    while( r != EOF){

       r = ReadTextLine(fp, buffer, BUFFER_SIZE);
    //    printf("%d %s\n", buffer_size, buffer);
       match = strstr (buffer, "model name");

       if (match !=NULL){
            /* Parse the line to extract the clock speed. */
            sscanf (match, "model name : %s", cpu_model);
            break;
       }
   }
   close(fp);

   return cpu_model;
}

The proc/cpuinfo file looks something like this:

processor:0
cpu core :1
model name: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz


回答1:


Out of the fact you use r non initialized when you enter in the loop with an undefined behavior, it is difficult to find your problem without knowing the definition of ReadTextLine.

The other answer speak about some possibile problems.

Here is a proposal to do the work :

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

char * get_cpu()
{
  FILE * fp = fopen("/proc/cpuinfo", "r");
  char line[256];
  char * result = NULL;

  if (fp == NULL) {
    /* low probability to happen */
    perror("cannot read /proc/cpuinfo");
    return NULL;
  }

  while (fgets(line, sizeof(line), fp) != NULL) {
    char * match = strstr(line, "model name");

    /* bypass ':' then possible spaces */
    if ((match != NULL) && ((match = strchr(match, ':')) != NULL)) {
      do
        match += 1;
      while (*match && isspace((unsigned char) *match));

      if (*match) {
        /* remove possible spaces including \r\n at end of line */
        char * pend = match + strlen(match);

        while (isspace((unsigned char) *--pend))
          *pend = 0;

        /* duplicate the string to not return an address in the stack */
        result = strdup(match);
        break;
      }
    }
  }

  fclose(fp);
  return result;
}

int main()
{
  char * s = get_cpu();

  if (s != NULL) {
    printf("(first) cpu is '%s'\n", s);
    /* the result was allocated, free it when not needed anymore */
    free(s);
  }

  return 0;
}

Compilation and execution :

pi@raspberrypi:/tmp $ gcc -Wall -Werror -pedantic c.c
pi@raspberrypi:/tmp $ ./a.out
(first) cpu is 'ARMv7 Processor rev 3 (v7l)'
pi@raspberrypi:/tmp $ 

In my case the beginning of /proc/cpuinfos is :

pi@raspberrypi:/tmp $ head -2 /proc/cpuinfo
processor   : 0
model name  : ARMv7 Processor rev 3 (v7l)
pi@raspberrypi:/tmp $ 

If you really want to have a function ReadTextLine it must do more than just a fgets, so let decide it also remove the spaces at the end of the line (it is useless to move spaces at the beginning of the line in this context)

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

char * ReadTextLine(FILE * fp, char * line, int size)
{
  if (fgets(line, size, fp) == NULL)
    return NULL;

  /* remove possible spaces including \r\n at end of line */
  char * pend = line + strlen(line);

  while ((pend != line) && isspace((unsigned char) *--pend))
    *pend = 0;

  return line;
}

char * get_cpu()
{
  FILE * fp = fopen("/proc/cpuinfo", "r");
  char line[256];
  char * result = NULL;

  if (fp == NULL) {
    /* probably never happend under linux */
    perror("cannot read /proc/cpuinfo");
    return NULL;
  }

  while (ReadTextLine(fp, line, sizeof(line)) != NULL) {
    char * match = strstr(line, "model name");

    /* bypass ':' then possible spaces */
    if ((match != NULL) && ((match = strchr(match, ':')) != NULL)) {
      do
        match += 1;
      while (*match && isspace((unsigned char) *match));

      if (*match) {
        result = strdup(match);
        break;
      }
    }
  }

  fclose(fp);
  return result;
}



回答2:


Your termination condition for the model name is "till the end of the line". Presumably ReadTextLine reads the entire line. So all you need is to find the beginning of the model name and strcpy it out of there:

match = strstr(buffer, "model name: ");
// ... match points to "model name: XXX"
if(match) {
    match += strlen("model name: ");
    // ... match points to "XXX"
    strcpy(cpu_model, match);
}

Note, however, that your code is using cpu_model without initializing it, which is a bug. You should either convert it to a parameter so that the caller would give you the buffer, or use cpu_model = strdup(match) to allocate the result on the heap.

As @bruno noted you're also using r before it's initialized. The correct condition would be:

while(ReadTextLine(fp, buffer, BUFFER_SIZE) != EOF)

so that you exit immediately when you get EOF and don't need r at all.




回答3:


cpu_model is never initialized so the line:

sscanf (match, "model name : %s", cpu_model);

doesn't have anywhere to store the data. (That is, it is trying to write to wherever cpu_model is currently pointing, which is most likely not a valid memory location.)

The easiest fix would be to change the declaration to:

char cpu_model[128];

(and appropriately restrict the format string to be %127s)



来源:https://stackoverflow.com/questions/60901289/alternative-for-sscanf-string-with-spaces-in-c

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!