在 C++ 中读取只有字符指针的文件
Read a file with only char pointers in c++
我需要将文件内容读入某些对象,不幸的是我不能随意使用 std::string,因此必须使用字符指针。
但是,当我这样做时,我会直接从内存中得到奇怪的迹象,但是 solution 是行不通的。
所以我通过直接从 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] == '[=10=]' || 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()
.
您正在泄漏 portLine
和 name
缓冲区。更糟糕的是,由于您不允许使用 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;
}
我需要将文件内容读入某些对象,不幸的是我不能随意使用 std::string,因此必须使用字符指针。 但是,当我这样做时,我会直接从内存中得到奇怪的迹象,但是 solution 是行不通的。 所以我通过直接从 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] == '[=10=]' || 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()
.
您正在泄漏 portLine
和 name
缓冲区。更糟糕的是,由于您不允许使用 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;
}