2010-03-15 3 views
3

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

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++ функциональность как потоки не могут помочь здесь

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

+0

Рассматривали ли вы использование разделяемой памяти или файла с отображением памяти? – jschmier

+0

Я не могу сказать, что у меня есть, хотя я не очень хорошо знаком с ними. Что-то вроде mmap() хорошее место для начала? – Dusty

ответ

2

Поскольку вы пытаетесь сохранить данные файла в памяти, вы должны иметь возможность использовать shared memory. Общая память 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; 
} 

Примечание: Я должен был пройти -lrt линкера при компиляции этого примера с GCC на Linux.

+0

Это будет сделано, спасибо! Есть ли какое-либо значение для данного имени, или это просто ручка? Я полагаю, так как это для IPC, что любой процесс с использованием одного и того же объекта получает один и тот же объект? Итак, чтобы избежать столкновений, мне нужно, чтобы имя дескриптора было уникальным? – Dusty

+0

Это правильно. * Имя * - это строка, называющая объект общей памяти. Для портативного использования * имя * должно иметь начальную косую черту (/) и не содержать встроенных косых черт. Дополнительную информацию см. В документации. – jschmier

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

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

  • Использование mmap() (Beej's Guide помощь)

+1

'mkstemp' использует свой аргумент как шаблон для имени временного файла, а не его содержимое - например,'/tmp/fileXXXXXX.tmp' превращается в '/ tmp/fileHa7110.tmp'. –

+0

Это было бы проще всего. Создайте временный файл, напишите ему строку и используйте оригинальный LoadFromFile(). Там очень мало нового кода и что новый код есть, прост. Другими словами, низкая вероятность появления новых ошибок и простота в обслуживании. – JayM

+1

Как это сделать, чтобы OP обходила запись конфигурации на диск и вместо этого передавала ее непосредственно в память? – jschmier

3

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

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

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

+0

fscanf перемещает указатель файла по мере его чтения, разрешая повторные и последовательные вызовы fscanf, sscanf этого не делает. Как реплицировать функциональность fscanf с помощью sscanf для перехода по строке? –

+1

Спецификатор формата '% n' заставит' scanf' и друзья записать количество символов, прочитанных (этим конкретным вызовом 'scanf') в соответствующий аргумент. Используйте указатель, чтобы отслеживать текущее местоположение чтения во входном буфере (эмулировать указатель файла) и использовать '% n' в конце каждой строки формата sscanf, чтобы вы знали, как далеко продвинуть указатель для следующего вызов 'sscanf'. – bta

+0

awesome, я никогда не видел '% n' used (+1) –

 Смежные вопросы

  • Нет связанных вопросов^_^