将文件内容读取到动态分配的 char* 数组中 - 我可以读取 std::string 吗?

Reading contents of file into dynamically allocated char* array- can I read into std::string instead?

本文关键字:读取 我可以 std string 数组 文件 动态分配 char      更新时间:2023-10-16

我发现自己在写代码看起来像这样

// Treat the following as pseudocode - just an example
iofile.seekg(0, std::ios::end); // iofile is a file opened for read/write
uint64_t f_len = iofile.tellg();
if(f_len >= some_min_length)
{
    // Focus on the following code here
    char *buf = new char[7];
    char buf2[]{"MYFILET"}; // just some random string
                            // if we see this it's a good indication
                            // the rest of the file will be in the
                            // expected format (unlikely to see this
                            // sequence in a "random file", but don't
                            // worry too much about this)
    iofile.read(buf, 7);
    if(memcmp(buf, buf2, 7) == 0) // I am confident this works
    {
        // carry on processing file ...
        // ...
        // ...
    }
}
else
    cout << "invalid file format" << endl;

这段代码可能是我们在打开文件时可能想要做什么的草图,该文件具有某种指定的格式(我已经口述(。我们做了一些初步检查,以确保字符串"MYFILET"位于文件的开头 - 因为我已经决定我正在做的工作的所有文件都将从这个字符序列开始。

我认为如果我们不必使用"c 样式"字符数组,而是在任何地方使用字符串,这段代码会更好。这将是有利的,因为我们可以做一些事情,比如if(buf == buf2)如果bufbuf2 std::string在哪里。

一个可能的替代方案可能是,

// Focus on the following code here
std::string buf;
std::string buf2("MYFILET"); // very nice
buf.resize(7); // okay, but not great
iofile.read(buf.data(), 7); // pretty awful - error prone if wrong length argument given
                            // also we have to resize buf to 7 in the previous step
                            // lots of potential for mistakes here,
                            // and the length was used twice which is never good
if(buf == buf2) then do something

这有什么问题?

  • 我们不得不使用长度变量7(在本例中为常量(两次。这介于"不理想"和"潜在容易出错"之间。
  • 我们必须使用 .data() 访问buf的内容,我假设这里实现它是为了返回某种原始指针。我个人不太介意这一点,但其他人可能更喜欢更内存安全的解决方案,也许暗示我们应该使用某种迭代器?我认为在Visual Studio中(对于我不是的Windows用户(,那么无论如何这都可能会返回一个迭代器,它会给出[?]警告/错误[?] - 不确定这一点。
  • 我们必须为buf有一个额外的调整大小语句。如果可以以某种方式自动设置buf的大小会更好。
写入

std::string::data()返回的const char*是未定义的行为。 但是,您可以通过这种方式自由使用std::vector::data()

如果你想使用 std::string,并且不喜欢自己设置大小,你可以考虑是否可以使用 std::getline() . 这是免费功能,不是std::istream::getline()std::string版本将读取指定的分隔符,因此如果您有文本格式,您可以告诉它读取直到''或其他一些永远不会出现的字符,它将自动调整给定字符串的大小以保存内容。

如果你的文件本质上是二进制的,而不是文本,我想大多数人会发现std::vector<char>std::string更自然。

我们不得不使用长度变量 7(在本例中为常数(两次。 这介于"不理想"和"潜在容易出错"之间。

第二次可以使用buf.size()

iofile.read(buf.data(), buf.size());

我们必须使用 .data(( 访问 buf 的内容,我将 假设此处实现为返回某种原始指针。

并由约翰·兹温克指出,.data()返回指向const的指针。

我想你可以buf定义为std::vector<char>;对于vector(如果我没记错的话(,.data()返回一个指向char(在这种情况下(的指针,而不是指向const char的指针。

size()resize()的工作方式相同。

我们必须为 buf 提供一个额外的调整大小语句。这将是 如果可以以某种方式自动设置 BUBF 的大小,那就更好了。

我认为read()不允许这样做。

PS:对不起,我的英语不好。

我们可以在没有双重缓冲(rdbuf 和字符串(的情况下验证签名并从堆中分配......

// terminating null not included
constexpr char sig[] = { 'M', 'Y', 'F', 'I', 'L', 'E', 'T' };     
auto ok = all_of(begin(sig), end(sig), [&fs](char c) { return fs.get() == (int)c; });
if (ok) {}
template<class Src>
std::string read_string( Src& src, std::size_t count){
  std::string buf;
  buf.resize(count);
  src.read(&buf.front(), 7); // in C++17 make it buf.data()
  return buf;
}

现在auto read = read_string( iofile, 7 );在使用点是干净的。

buf2是一个糟糕的计划。 我会做的:

if(read=="MYFILET")

直接,或使用const char myfile_magic[] = "MYFILET";.

我喜欢

上面例子中的许多想法,但我并不完全满意有一个答案可以为 C++11 和 C++17 生成无定义行为的代码。我目前用 C++11 编写大部分代码 - 因为我预计将来不会在没有 C++11 编译器的机器上使用它。

如果没有,那么我会添加新的编译器或更换机器。

然而,在我看来,编写我知道在

C++17 下可能不起作用的代码确实是一个坏主意......这只是我个人的看法。我预计不会再次使用此代码,但我不想将来给自己带来潜在的问题。

因此,我想出了以下代码。我希望其他用户能提供反馈以帮助改进这一点。(例如,尚无错误检查。

std::string
fstream_read_string(std::fstream& src, std::size_t n)
{
    char *const buffer = new char[n + 1];
    src.read(buffer, n);
    buffer[n] = '';
    std::string ret(buffer);
    delete [] buffer;
    return ret;
}

这似乎是一种基本的,可能是万无一失的方法......遗憾的是,似乎没有办法让 std::string 使用与调用 new 分配的相同内存。

请注意,我们必须在 C 样式字符串中添加一个额外的尾随 null 字符,该字符在 C++ 样式std::string中被切掉。