您拥有的功能会检查文件是否可以打开,但对于某些存在但您无权打开的文件,它会失败。我会改用stat。要连接路径和文件名,您可以使用字符串函数。
通常的 Unix C API 很糟糕。正确地做最简单的事情需要付出很多努力——即便如此,我也不确定我没有忘记一些 Unix 主义,比如信号处理或一些晦涩的错误案例。 IE。在现代 C++ 中处理起来相当简单的东西。
我希望有人设计一个现代 C 系统 API 并至少在 Linux 上实现它,这样我们的痛苦就会结束……
通常,字符串连接需要一些更高级别的 API 来完成,同时保持一定程度的理智。因此,下面的示例使用strbuilder 类来构建字符串。这使事情变得模糊可读,并避免了最常见的错误。
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct strbuilder {
unsigned items, item;
size_t length, *lengths;
char *str, *dst;
};
bool strbuilder_pass(struct strbuilder *builder, int *rc);
void strcat_str(struct strbuilder *builder, const char *src);
void strcat_c_ifnone(struct strbuilder *builder, char c);
bool strbuilder_is_freed(const struct strbuilder *builder);
int fileExists(const char *path, const char *filename)
{
const char pathSep = '/';
int rc;
struct strbuilder bld = {0};
while (strbuilder_pass(&bld, &rc))
{
strcat_str(&bld, path);
strcat_c_ifnone(&bld, pathSep);
strcat_str(&bld, filename);
if (!rc)
{
struct stat statbuf;
printf("path = %s\n", bld.str);
rc = stat(bld.str, &statbuf);
}
}
assert(strbuilder_is_freed(&bld));
return rc;
}
int main()
{
int rc = fileExists("/", "dev");
assert(rc == 0);
return 0;
}
字符串构建由strbuilder_pass 函数控制,该函数通过五次操作推进字符串构建器的状态:
- 确定必须存储宽度的项目数(避免调用 strlen 两次)。
- 准备长度存储向量。确定所需缓冲区的长度。
- 准备输出字符串缓冲区。将元素连接到缓冲区中。
- 使用输出字符串缓冲区。
- 释放输出字符串缓冲区。
这个 API 不是特别特别,但适合这个用例。其他一些临时方法也可以,但恕我直言,这更优雅一些。
void strbuilder_free(struct strbuilder *builder)
{
free(builder->lengths);
free(builder->str);
memset(builder, 0, sizeof(*builder));
}
bool strbuilder_pass(struct strbuilder *builder, int *rc)
{
if (!builder->length) {// start of pass 1
builder->length = 1; /*term*/
*rc = EAGAIN;
return true;
}
else if (!builder->lengths) // end of pass 1
{
builder->lengths = malloc(sizeof(*builder->lengths) * builder->items);
if (builder->lengths)
return true;
*rc = ENOMEM;
}
else if (!builder->str) // end of pass 2
{
builder->dst = (builder->str = malloc(builder->length));
builder->item = 0;
builder->length = 0;
if (builder->dst) {
*builder->dst = '\0';
return true;
}
*rc = ENOMEM;
}
else if (builder->dst) // end of pass 3
{
while (*builder->dst) { // include optional content
builder->dst++; // skip
builder->length++;
}
builder->dst = NULL;
*rc = 0;
return true;
}
else if (!builder->dst) // end of pass 4 (if any)
{}
else {
*rc = EINVAL;
}
strbuilder_free(builder);
return false;
}
void strcat_str(struct strbuilder *builder, const char *src)
{
if (!src)
return;
if (!builder->lengths) // pass 1
builder->items ++;
else if (!builder->str) // pass 2
{
size_t len = strlen(src);
builder->lengths[builder->item++] = len;
builder->length += len;
}
else if (builder->dst) // pass 3
{
size_t len = builder->lengths[builder->item++];
if (*builder->dst && (!len || *builder->dst != *src))
{
builder->dst++;
builder->length++;
}
memcpy(builder->dst, src, len);
builder->dst += len;
builder->length += len;
*builder->dst = '\0';
}
}
void strcat_c_ifnone(struct strbuilder *builder, char c)
{
if (!builder->lengths) {} // pass 1
else if (!builder->str) // pass 2
{
if (c) builder->length ++;
}
else if (builder->dst) // pass 3
{
if (!builder->length || builder->dst[-1] != c)
*(builder->dst) = c;
}
}
bool strbuilder_is_freed(const struct strbuilder *builder)
{
return !builder || (!builder->lengths && !builder->str);
}