在 c++ 中读取仅包含字符指针的文件

Read a file with only char pointers in c++

本文关键字:字符 指针 文件 包含 c++ 读取      更新时间:2023-10-16

我需要将文件内容读入某些对象,不幸的是,我不能自由使用 std::string,因此必须使用 char 指针。但是,当我这样做时,我直接从内存中得到奇怪的迹象,但是解决方案不起作用。所以我直接从 istream 而不是 std 库使用 getline 重建,但同样的事情发生了。如何在不使用 std::string 的情况下正确读取文件。

PortsContainer game::ParsePort(std::istream& stream)
{
    PortsContainer ports;
    bool passFirstRow = false;
    char* portLine = new char[1000000];
    int i = 0;
    while (!stream.eof())
    {
        if (!stream)
            throw std::system_error(Error::STREAM_ERROR);
        if (portLine[0] == '' || portLine == nullptr || portLine[0] == '#')
            continue;
        std::stringstream ss(portLine);
        if (!passFirstRow) {
            char* name = new char[100];
            while (!ss.eof()) {
                ss.getline(name, sizeof name, ';');
                Port* port = new Port();
                //port->name = const_cast<char*>(name);
                port->name = const_cast<char*>(name);
                ports.addItem(port);
            }
            passFirstRow = true;
        } else {
            i++;
        }
        if (!stream)
            throw std::system_error(Error::STREAM_ERROR);
    }
    return ports;
}
PortsContainer game::ParsePort(std::istream& stream, std::error_code& errorBuffer)
{
    try
    {
        return ParsePort(stream);
    }
    catch (std::system_error exception)
    {
        errorBuffer = exception.code();
    }
}
PortsContainer game::GetAvailablePorts()
{
    PortsContainer ports;
    std::ifstream stream("./ports.csv");
    std::error_code errorBuffer;
    ports = ParsePort(stream, errorBuffer);
    if (errorBuffer)
        return PortsContainer();
    return ports;
}

您没有用任何数据填充portLine。 实际上,您根本没有从stream读取任何数据。

您滥用eof(). 在首先尝试读取操作之前,eofbit标志不会更新。 所以你必须在eof()之前阅读.

您正在泄漏portLinename缓冲区。 更糟糕的是,由于您不允许使用 std::string ,这意味着 Port::name 成员是一个char*指针,这意味着您(可能)有多个Port对象指向内存中的同一物理缓冲区。 如果Port稍后尝试释放该缓冲区(例如在其析构函数中),则会出现内存错误。

尝试更多类似的东西:

PortsContainer game::ParsePort(std::istream& stream)
{
    if (!stream)
        throw std::system_error(Error::STREAM_ERROR);
    PortsContainer ports;
    bool passFirstRow = false;
    // better would be to use std::unique_ptr<char[]>, std::vector<char>,
    // or std::string instead so the memory is freed automatically ...
    char *portLine = new char[1000000];
    int i = 0;
    do
    {
        if (!stream.getline(portLine, 1000000))
        {
            delete[] portLine; // <-- free the buffer for line data...
            throw std::system_error(Error::STREAM_ERROR);
        }
        if ((stream.gcount() == 0) || (portLine[0] == '#'))
            continue;
        if (!passFirstRow)
        {
            std::istringstream iss(portLine);
            // better would be to use std::unique_ptr<char[]>, std::vector<char>,
            // or std::string instead so the memory is freed automatically ...
            char* name = new char[100];
            while (iss.getline(name, 100, ';'))
            {
                if (iss.gcount() == 0) continue;
                Port *port = new Port();
                port->name = name; // <-- assumes ownership is transferred!
                ports.addItem(port);
                name = new char[100]; // <-- have to reallocate a new buffer each time!
            }
            delete[] name; // <-- free the last buffer not used...
            passFirstRow = true;
        } else {
            ++i;
        }
    }
    while (!stream.eof());
    delete[] portLine; // <-- free the buffer for line data...
    return ports;
}
PortsContainer game::ParsePort(std::istream& stream, std::error_code& errorBuffer)
{
    try
    {
        return ParsePort(stream);
    }
    catch (const std::system_error &exception)
    {
        errorBuffer = exception.code();
        return PortsContainer(); // <-- don't forget to return something!
    }
}
PortsContainer game::GetAvailablePorts()
{
    std::ifstream stream("./ports.csv");
    std::error_code errorBuffer;
    return ParsePort(stream, errorBuffer); // <-- no need to check errorBuffer before returning!
}

但是,我强烈建议您使用 STL 智能指针来帮助确保更安全的内存管理:

PortsContainer game::ParsePort(std::istream& stream)
{
    if (!stream)
        throw std::system_error(Error::STREAM_ERROR);
    PortsContainer ports;
    bool passFirstRow = false;
    // since you are using std::error_code, that means you are
    // using C++11 or later, so use std::unique_ptr to ensure
    // safe memory management...
    std::unique_ptr<char[]> portLine(new char[1000000]);
    int i = 0;
    do
    {
        if (!stream.getline(portLine.get(), 1000000))
            throw std::system_error(Error::STREAM_ERROR);
        if ((stream.gcount() == 0) || (portLine[0] == '#'))
            continue;
        if (!passFirstRow)
        {
            std::istringstream iss(portLine.get());
            // use std::unique_ptr here, too...
            std::unique_ptr<char[]> name(new char[100]);
            while (iss.getline(name.get(), 100, ';'))
            {
                if (iss.gcount() == 0) continue;
                // use std::unique_ptr here, too...
                std::unique_ptr<Port> port(new Port);
                port->name = name.release(); // <-- assumes ownership is transferred!
                                             // better to make Port::name use std::unique_ptr<char[]> and then std::move() ownership of name to it...
                ports.addItem(port.get());
                port.release();
                name.reset(new char[100]); // <-- have to reallocate a new buffer each time!
            }
            passFirstRow = true;
        } else {
            ++i;
        }
    }
    while (!stream.eof());
    return ports;
}

不过,使用 std::string 将是最好的选择:

PortsContainer game::ParsePort(std::istream& stream)
{
    if (!stream)
        throw std::system_error(Error::STREAM_ERROR);
    PortsContainer ports;
    bool passFirstRow = false;
    std::string portLine;
    int i = 0;
    while (std::getline(stream, portLine))
    {
        if (portLine.empty() || (portLine[0] == '#'))
            continue;
        if (!passFirstRow)
        {
            std::istringstream iss(portLine);
            std::string name;
            while (std::getline(iss, name, ';'))
            {
                if (name.empty()) continue;
                std::unique_ptr<Port> port(new Port);
                port->name = name; // <-- make Port::name be std::string as well!
                ports.addItem(port.get());
                port.release();
            }
            passFirstRow = true;
        } else {
            ++i;
        }
    }
    if (!stream)
        throw std::system_error(Error::STREAM_ERROR);
    return ports;
}