我有一个格式如下的文件:
1: some_basic_info_in_this_line
2: LOTS_OF_INFO_IN_THIS_LINE_HUNDREDS_OF_CHARS
3: some_basic_info_in_this_line
4: LOTS_OF_INFO_IN_THIS_LINE_HUNDREDS_OF_CHARS
...
这种格式重复数万次,文件最多可达50 GiB +。我需要一种有效的方法来处理这种格式的第2行。我愿意使用C,C ++ 11 STL或者提升。我已经查看了有关SO上的文件流的各种其他问题,但我觉得我的情况是独特的,因为文件很大,只需要每四行中就有一行。
从我读过的内容映射文件似乎是最有效的,但是映射50+ GB文件会占用大多数计算机RAM(你可以假设这个应用程序将被"平均"用户 - 比如4-8 GiB RAM)。此外,我只需要一次处理其中一行。以下是我目前的做法(是的,我知道这不是有效的,这就是我重新设计它的原因):
std::string GL::getRead(ifstream& input)
{
std::string str;
std::string toss;
if (input.good())
{
getline(input, toss);
getline(input, str);
getline(input, toss);
getline(input, toss);
}
return str;
}
将mmap打破成块可以解决我的问题吗?反正我是否只能利用4行中的1行?谢谢你的帮助。
答案 0 :(得分:5)
使用ignore
代替getline
:
std::string GL::getRead(ifstream& input)
{
std::string str;
if (!input.fail())
{
input.ignore(LARGE_NUMBER, '\n');
getline(input, str);
input.ignore(LARGE_NUMBER, '\n');
input.ignore(LARGE_NUMBER, '\n');
}
return str;
}
LARGE_NUMBER可能是std::numeric_limits<std::streamsize>::max()
如果您没有充分理由拥有较小的号码(想想DOS攻击)
提示考虑通过引用传递
str
。通过每次读取相同的字符串,您可以避免大量分配,这通常是程序运行缓慢的第一个原因。提示考虑使用备忘录映射文件(Boost Iostreams,Boost Interpocess或
mmap(1)
)
答案 1 :(得分:2)
内存映射文件不会将其加载到RAM中。它占用了进程的虚拟地址空间,但不占用物理RAM。 mmap
系统调用将在32位系统上失败,因为4GiB的虚拟地址空间不足以容纳50GiB文件。在64位系统上,它需要几微秒。 (没有磁盘读取,因为文件已经打开,因此文件元数据已经加载。)
只有您实际读取的页面才会从磁盘加载,并且只要操作系统想要回收某些内存,就可以再次取消映射页面。 (因为如果你以后再次阅读它们,操作系统可以从磁盘重新加载。就像将它们交换到交换空间/页面文件一样,但不必写,因为磁盘上已有一个干净的副本。)
内存映射可让您的进程读取操作系统的页面缓存页面,而不是通过read
系统调用来复制它们。
Have a look at wikipedia了解更多信息。
答案 2 :(得分:0)
这是我能提出的最有效的解决方案,它与平台无关。我想到了所有的用户,现在我假设每个人都有64位机器,如果他们使用4 + GiB文件大小。如果这种情况发生变化,我将不得不修改该类以将“块”数据处理到单独的mmap
区域。
#include <string>
#include <boost/iostreams/device/mapped_file.hpp>
//////////////////////////////////////////////////////////////////////////////////////////
/// @class LineParser
///
/// @brief Class to efficiently parse a file and take the second line out of every 4 lines
///
/// This class uses memory-mapped io to efficiently extract and return sequences from a
/// file
//////////////////////////////////////////////////////////////////////////////////////////
class LineParser
{
private:
boost::iostreams::mapped_file mmap; ///< Object for memory mapped file
const char* curr; ///< Current position of the file
const char* end; ///< End position of the file
public:
//////////////////////////////////////////////////////////////////////////////////////
/// @fn valid
///
/// Indicates whether the parser is in a valid state or not
///
/// @return Boolean indicating if the parser is open and in a valid state
///
/// @note Declared inline as it is acceptable in my situation because of being called
/// many times in only a few spots
//////////////////////////////////////////////////////////////////////////////////////
inline bool valid(void)
{
return (curr && end && (curr < end) && (mmap.is_open()));
}
//////////////////////////////////////////////////////////////////////////////////////
/// @fn peek
///
/// Obtains the next sequence string - if it exists - but maintains parsers state
///
/// @return Next sequence available in the file. Emptry string returned if none
/// exist
///
/// @note Declared inline as it is acceptable in my situation because of being called
/// many times in only a few spots
//////////////////////////////////////////////////////////////////////////////////////
inline std::string peek(void)
{
const char* save = curr;
std::string ret;
getRead(ret);
curr = save;
return ret;
}
//////////////////////////////////////////////////////////////////////////////////////
/// @fn getRead
///
/// Sets container to the current read being processed
///
/// @param container String container to place current sequence into
///
/// @return Boolean indicating if getting a new read was successful
///
/// @note Declared inline as it is acceptable in my situation because of being called
/// many times in only a few spots
//////////////////////////////////////////////////////////////////////////////////////
inline bool getRead(std::string& container)
{
if (valid() == false)
{
return false;
}
curr = static_cast<const char*>(memchr(curr, '\n', end-curr)) + 1;
const char* index = static_cast<const char*>(memchr(curr, '\n', end-curr));
container = std::string(curr, index - curr);
curr = index + 1;
curr = static_cast<const char*>(memchr(curr, '\n', end-curr)) + 1;
curr = static_cast<const char*>(memchr(curr, '\n', end-curr)) + 1;
return true;
}
//////////////////////////////////////////////////////////////////////////////////////
/// @fn LineParser
///
/// Constructor to initialize memory mapped file and set index values
//////////////////////////////////////////////////////////////////////////////////////
LineParser(const std::string& filepath)
: mmap(filepath, boost::iostreams::mapped_file::readonly)
{
if (mmap.is_open())
{
curr = mmap.const_data();
end = curr + mmap.size();
}
}
//////////////////////////////////////////////////////////////////////////////////////
/// @fn ~LineParser
///
/// Default destructor
//////////////////////////////////////////////////////////////////////////////////////
~LineParser(void) = default;
};
请注意,这个类并不完美,可能无法很好地处理文件格式的偏差,但在完美文件格式的假设下,它可以很好地工作。