0

我正在尝试使用 fopen 打开一个文件,但我不想要一个静态位置,所以当他/她运行程序时我从用户那里获取字符串。但是,如果用户不输入,则指定默认文件。

我可以将 malloc var 放入 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);

正如您可能已经猜到的那样,我是新手,所以如果我做错了可怕的事情,那么对不起。

4

6 回答 6

3

这不是你认为的那样:

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

您要做的是更改第二行以将默认名称复制到分配的缓冲区中:

strcpy(file_path_mem, "marks.txt");
于 2009-11-19T21:50:01.827 回答
3

您可以将从 malloc 返回的 char* 直接传递给 fopen。只要确保它包含有效数据。尽管请确保您按照 R Samuel 指出的方式输入新字符串。

顺便说一句,您对 realloc 的使用会带来非常糟糕的性能。Realloc 的工作原理是查看是否有足够的空间来增长内存(这不是保证,realloc 必须只返回一个包含旧数据的 newSize 块)。如果没有足够的空间,它会分配一个新大小的新块,并将旧数据复制到新块。显然这是次优的。

你最好分配一个块,比如 16 个字符,然后,如果你需要超过 16 个,再重新分配 16 个字符。会有一些内存浪费,但您可能不会复制任何类似内存的东西。

编辑:除此之外。对于包含 7 个字符的字符串。使用 realloc 每个字节方案,您将生成以下过程。

alloc 1 字节
alloc 2 字节
复制 1 字节
空闲 1 字节
分配 3 字节
复制 2 字节
空闲 2 字节
分配 4 字节
复制 3 字节
空闲 3 字节
分配 5 字节
复制 4 字节
空闲 4 字节
分配 6 字节
复制 5 字节
空闲 5 字节
分配 7字节
复制 6 字节
空闲 6 字节
分配 8 字节
复制 7 字节

这是 8 个分配、7 个空闲和 28 个字节的副本。更不用说分配给数组的 8 个字节(7 个字符 + 1 个空终止符)。

分配很慢。免费是缓慢的。复制比不复制要慢得多。

对于此示例,使用我一次分配 16 个字节的系统,您分配一次,分配 8 个字节。没有副本,唯一的免费是当你完成它时。你确实浪费了 8 个字节。但是......好吧...... 8个字节在宏伟的计划中算不了什么......

于 2009-11-19T21:52:57.677 回答
2

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);
    }
  }
于 2009-11-19T22:01:21.087 回答
1

在这里应该不是问题,但是对于重新分配,您应该像这样声明新大小

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

于 2009-11-19T22:03:36.510 回答
0

假设它在您的系统上可用,您可以使用 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 教程。

于 2009-11-19T22:06:07.410 回答
0

作为一种学习经验,我会支持其他人的建议,将您的缓冲区扩大一个字节以上。如果它不在循环中,但在每个字节上调用 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");
}
于 2009-11-19T22:23:34.007 回答