Открытие файла с путем в malloc

Я пытаюсь открыть файл с помощью fopen, но мне не нужно статическое местоположение, поэтому я получаю строку от пользователя, когда он/она запускает программу. Однако, если пользователь не вводит его, указывается файл по умолчанию.

Могу ли я просто поместить переменную malloc в параметр пути fopen?

char *file_path_mem = malloc(sizeof(char));
if (file_path_mem != NULL) //Null if out of memory
{
  printf("Enter path to file, if in current directory then specify name\n");
  printf("File(default: marks.txt): ");

  while ((c = (char)getchar()) != '\n')
  {
    file_path_mem[i++] = c;
    file_path_mem = realloc(file_path_mem, i+1 * sizeof(char));
  }
    file_path_mem[i] = '\0';
    if (i == 0 && c == '\n')
    {
      file_path_mem = realloc(file_path_mem, 10 * sizeof(char);
      file_path_mem = "marks.txt";
    }
  }
  else
  {
    printf("Error: Your system is out of memory, please correct this");
    return 0;
  }
  if (i==0)
  {
    FILE *marks_file = fopen("marks.txt", "r");
  }
  else
  {
    FILE *marks_file = fopen(file_path_mem, "r");
  }
  free(file_path_mem);

Как вы могли догадаться, я новичок, поэтому, если я сделал что-то ужасное неправильно, извините.


person Botto    schedule 19.11.2009    source источник


Ответы (6)


realloc относительно дороже, чем остальная часть вашего цикла, поэтому вы можете просто начать со 128-байтового буфера и перераспределить еще 128 байтов, если вы его заполните.

Я предлагаю некоторые из следующих изменений:

определите свое местоположение по умолчанию в верхней части файла

char* defaultLocation = 'myfile.txt';
char* locationToUse;

используйте константы вместо цифр жесткого кодирования в вашем коде (например, 10, которые у вас есть)

int DEFAULT_INPUT_BUFFER_SIZE = 128;
char* userInputBuffer = malloc(sizeof(char) * DEFAULT_INPUT_BUFFER_SIZE) );
int bufferFillIndex = 0;

перераспределять нечасто и не перераспределять вообще, если это возможно (размер буфера 128)

while ((c = (char)getchar()) != '\n')
  {
    file_path_mem[bufferFillIndex++] = c;
    if (bufferFillIndex % DEFAULT_INPUT_BUFFER_SIZE == 0)
    {
        realloc(file_path_mem, (bufferFillIndex + DEFAULT_INPUT_BUFFER_SIZE) * sizeof(char);
    }
  }
person Zak    schedule 19.11.2009

Это не то, что вы думаете:

file_path_mem = realloc(file_path_mem, 10 * sizeof(char);
file_path_mem = "marks.txt";

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

strcpy(file_path_mem, "marks.txt");
person R Samuel Klatchko    schedule 19.11.2009

Вы МОЖЕТЕ передать char*, возвращенный из malloc, прямо в fopen. Просто убедитесь, что он содержит достоверные данные. Хотя убедитесь, что вы загрузили новую строку, как указывает R Samuel.

Кстати, ваше использование realloc даст довольно ужасную производительность. Realloc работает, проверяя, достаточно ли места для увеличения памяти (это не гарантируется, realloc должен просто возвращать блок, который является новым размером, содержащим старые данные). Если места недостаточно, он выделяет новый блок нового размера и копирует старые данные в новый блок. Очевидно, что это неоптимально.

Вам лучше выделить блок, скажем, из 16 символов, а затем, если вам нужно больше 16, перераспределить еще 16 символов. Будет небольшая трата памяти, но потенциально вы не копируете столько памяти.

Редактировать: В дополнение к этому. Для строки, содержащей 7 символов. Используя схему перераспределения каждого байта, вы сгенерируете следующие процессы.

выделить 1 байт
выделить 2 байта
скопировать 1 байт
освободить 1 байт
выделить 3 байта
скопировать 2 байта
освободить 2 байта
выделить 4 байта
скопировать 3 байты
свободно 3 байта
выделить 5 байтов
скопировать 4 байта
освободить 4 байта
выделить 6 байтов
скопировать 5 байтов
освободить 5 байтов
выделить 7 байтов< br> скопировать 6 байт
освободить 6 байт
выделить 8 байт
скопировать 7 байт

Это 8 выделений, 7 освобождений и копия 28 байт. Не говоря уже о 8 байтах, которые вы назначаете массиву (7 символов + 1 нулевой терминатор).

Распределения МЕДЛЕННЫЕ. Бесплатно МЕДЛЕННО. Копирование намного медленнее, чем не копирование.

В этом примере, используя мое выделение 16 байтов в системе времени, которую вы выделяете один раз, назначьте 8 байтов. Нет копий, и только когда вы закончили с ним. Однако вы ДЕЙСТВИТЕЛЬНО тратите 8 байтов. Но... ну... 8 байт - это ничто по большому счету...

person Goz    schedule 19.11.2009
comment
Распределения МЕДЛЕННЫЕ. Верно, но то же самое относится и к вводу-выводу. - person Adrian McCarthy; 20.11.2009

Здесь не должно быть проблем, но для перемещения вы должны указать новый размер следующим образом.

file_path_mem = realloc(file_path_mem, (i+1) * sizeof(char));

person caahab    schedule 19.11.2009

Вы можете значительно упростить это с помощью getline(), предполагая, что он доступен в вашей системе:

printf("Enter path to file, if in current directory then specify name\n");
printf("File(default: marks.txt): ");

int bufSize = 100;
char* fileNameBuf = (char*) malloc(bufSize + 1);
int fileNameLength = getline(&fileNameBuf, &bufSize, stdin);

if(fileNameLength < 0)
{
    fprintf(stderr, "Error: Not enough memory.\n")
    exit(1);
}

/* strip line end and trailing whitespace. */
while(fileNameLength && fileNameBuf[fileNameLength - 1] <= ' ')
    --fileNameLength;
fileNameBuf[fileNameLength] = 0;

if(!fileNameLength)
{
    free(fileNameBuf); /* Nothing entered; use default. */
    fileNameBuf = "marks.txt";
}

FILE *marks_file = fopen(fileNameBuf, "r");

if(fileNameLength)
    free(fileNameBuf);

Я не знаю, поддерживает ли ваш C внутриблочные объявления, поэтому я позволю вам исправить это, если это необходимо.

РЕДАКТИРОВАТЬ: Вот руководство по getline.

person Mike DeSimone    schedule 19.11.2009

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

Один трюк, который работает довольно хорошо, заключается в удвоении размера буфера каждый раз, когда он заполняется.

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

FILE *read_and_open(void)
{
  char *s, space[1000];

  printf("Enter path to file, if in current directory then specify name\n");
  printf("File(default: marks.txt): ");

  fgets(space, sizeof space, stdin);
  if((s = strchr(space, '\n')) != NULL)
    *s = 0;
  if (strlen(space) == 0)
    return fopen("marks.txt", "r");
  return fopen(space, "r");
}
person DigitalRoss    schedule 19.11.2009