альтернатива для строки sscanf с пробелами в c? - PullRequest
0 голосов
/ 28 марта 2020

Я пытаюсь получить информацию из файла / proc / cpuinfo. Я получил номер ядра процессора с помощью sscanf.

Теперь я пытаюсь получить название модели аналогичным образом, но в этот раз sscanf не работает, потому что имя модели - это строка, включающая пробелы.

Есть ли альтернатива для его получения?

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;
}

Файл proc / cpuinfo выглядит примерно так:

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

Ответы [ 3 ]

1 голос
/ 28 марта 2020

cpu_model никогда не инициализируется, поэтому строка:

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

не имеет места для хранения данных. (То есть он пытается записать туда, куда указывает cpu_model, что, скорее всего, не является допустимым местом в памяти.)

Самым простым решением было бы изменить объявление на:

char cpu_model[128];

(и соответственно ограничить строку формата %127s)

0 голосов
/ 28 марта 2020

Поскольку вы используете r без инициализации при вводе в l oop с неопределенным поведением, трудно найти вашу проблему, не зная определения ReadTextLine .

Другой ответ говорит о некоторых возможных проблемах.

Вот предложение сделать работу:

#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;
}

Компиляция и выполнение:

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 $ 

В моем случае начало / proc / cpuinfos начинается с:

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

Если вы действительно хотите иметь функцию ReadTextLine , она должна делать больше, чем просто fgets , поэтому давайте решим, что он также удаляет пробелы в конце строки (в этом контексте бесполезно перемещать пробелы в начале строки)

#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;
}
0 голосов
/ 28 марта 2020

Условием завершения для названия модели является «до конца строки». Предположительно ReadTextLine читает всю строку. Поэтому все, что вам нужно, это найти начало названия модели и strcpy его оттуда:

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);
}

Обратите внимание, однако, что ваш код использует cpu_model без его инициализации, что является ошибка. Вы должны либо преобразовать его в параметр, чтобы вызывающая сторона выдала вам буфер, либо использовать cpu_model = strdup(match) для выделения результата в куче.

Как заметил @bruno, вы также используете r перед тем это инициализировано. Правильным условием будет:

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

, чтобы вы немедленно вышли, когда получите EOF и вам вообще не понадобится r.

...