Функция преобразования для чтения из строки вместо файла в C - PullRequest
3 голосов
/ 16 марта 2010

Мне было поручено обновить функцию, которая в настоящее время читает файл конфигурации с диска и заполняет структуру:

static int LoadFromFile(FILE *Stream, ConfigStructure *cs)
{
  int tempInt;

   ...

  if ( fscanf( Stream, "Version: %d\n",&tempInt) != 1 )
  {
    printf("Unable to read version number\n");
    return 0;
  }
  cs->Version = tempInt;
   ...

}

на тот, который позволяет нам обойти запись конфигурации на диск и вместо этого передать ее непосредственно в память, что примерно эквивалентно этому:

static int LoadFromString(char *Stream, ConfigStructure *cs)

Несколько замечаний:

  • Текущая функция LoadFromFile невероятно плотная и сложная, считывает десятки версий конфигурационного файла обратно совместимым способом, что делает дублирование всей логики весьма болезненным.
  • Функции, которые генерируют файл конфигурации, и те, которые его читают, берутся из совершенно разных частей старой системы и, следовательно, не разделяют структуры данных, поэтому я не могу передать их напрямую. Я мог бы написать обертку, но опять же, она должна была бы обрабатывать любую переданную структуру обратно совместимым способом.
  • У меня возникает искушение просто передать файл как есть в виде строки (как в приведенном выше прототипе) и преобразовать все fscanf в sscanf, но затем я должен обработать увеличение указателя вместе (и потенциально иметь дело с ошибками переполнения буфера ) вручную.
  • Это должно остаться в C, поэтому никакие функции C ++, такие как потоки, не могут помочь здесь

Мне не хватает лучшего варианта? Есть ли способ создать ФАЙЛ *, который на самом деле просто указывает на место в памяти, а не на диске? Любые указатели, предложения или другая помощь с благодарностью.

Ответы [ 3 ]

3 голосов
/ 16 марта 2010

Если вы не можете передавать структуры и должны передавать данные в виде строки, тогда вы сможете настроить функцию для чтения из строки, а не из файла. Если функция настолько сложна, как вы описываете, то преобразование fscanf -> sscanf, возможно, будет самым простым способом.

Вот идея использования вашего прототипа функции выше. Считайте всю строку данных (не обрабатывая ее) и сохраните в локальном буфере. Таким образом, код может иметь произвольный доступ к данным, как и в случае с файлом, что упрощает прогнозирование и предотвращение переполнения буфера. Начните с malloc, поместив буфер разумного размера, скопируйте в него данные и realloc выделите себе больше места по мере необходимости. Получив локальную копию всего буфера данных, просканируйте его и извлеките все необходимые данные.

Обратите внимание, что это может быть сложно, если '\0' символов являются допустимыми ввода. В этом случае вам придется добавить дополнительную логику для проверки, является ли это концом входной строки или просто нулевым байтом (сложность зависит от конкретного формата буфера данных).

2 голосов
/ 16 марта 2010

Поскольку вы пытаетесь сохранить данные файла в памяти, вы сможете использовать разделяемую память . Совместно используемая память POSIX - это вариация отображаемой памяти. Объект общей памяти может быть отображен в адресное пространство процесса с помощью mmap () , если необходимо. Общая память обычно используется в качестве механизма IPC, но вы должны иметь возможность использовать ее в своей ситуации.

В следующем примере кода используется общая память POSIX ( shm_open () & shm_unlink () ) в сочетании с FILE * записать текст в объект общей памяти и затем прочитать его обратно.

#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>

#define MAX_LEN 1024

int main(int argc, char ** argv)
{
    int    fd;
    FILE * fp;
    char * buf[MAX_LEN];

    fd = shm_open("/test", O_CREAT | O_RDWR, 0600);

    ftruncate(fd, MAX_LEN);

    fp = fdopen(fd, "r+");

    fprintf(fp, "Hello_World!\n");

    rewind(fp);

    fscanf(fp, "%s", buf);

    fprintf(stdout, "%s\n", buf);

    fclose(fp);

    shm_unlink("/test");

    return 0;
}

Примечание. При компиляции этого примера с gcc в Linux мне пришлось передать -lrt компоновщику.

1 голос
/ 16 марта 2010
  • Используйте mkstemp() для создания временного файла. Он принимает char * в качестве аргумента и использует его в качестве шаблона для имени файла. Но он вернет файловый дескриптор.

  • Использование tmpfile(). Он возвращает FILE *, но имеет некоторые проблемы безопасности , а также вы должны скопировать строку самостоятельно.

  • Используйте mmap() ( Руководство Биджа для помощи)

...