我想用 std::istream 或 fscanf() 读取指定格式的文件。
文件的每一行都由几个字段组成。这些字段可以是字符、浮点数或整数。每个字段都有固定的宽度,可能为空。有没有办法阻止 std::istream 忽略空字段?
==================================================== ================
以下是详细说明。
我正在编写一个解析 pdb 样式文件的程序。部分格式遵循以下格式: 记录格式
COLUMNS DATA TYPE FIELD DEFINITION
------------------------------------------------------------------------------------
1 - 6 Record name "ATOM "
7 - 11 Integer serial Atom serial number.
13 - 16 Atom name Atom name.
17 Character altLoc Alternate location indicator.
18 - 20 Residue name resName Residue name.
22 Character chainID Chain identifier.
23 - 26 Integer resSeq Residue sequence number.
27 AChar iCode Code for insertion of residues.
31 - 38 Real(8.3) x Orthogonal coordinates for X in Angstroms.
39 - 46 Real(8.3) y Orthogonal coordinates for Y in Angstroms.
47 - 54 Real(8.3) z Orthogonal coordinates for Z in Angstroms.
55 - 60 Real(6.2) occupancy Occupancy.
61 - 66 Real(6.2) tempFactor Temperature factor.
77 - 78 LString(2) element Element symbol, right-justified.
79 - 80 LString(2) charge Charge on the atom.
这是实际输入的一部分:
ATOM 1 N MET A 0 24.512 8.259 -9.688 1.00 33.83 N
ATOM 2 CA MET A 0 24.523 9.740 -9.865 1.00 32.90 C
ATOM 3 C MET A 0 25.889 10.228 -10.330 1.00 31.90 C
ATOM 4 O MET A 0 26.886 9.516 -10.198 1.00 32.07 O
ATOM 5 CB MET A 0 24.143 10.414 -8.560 1.00 34.34 C
ATOM 6 CG MET A 0 24.891 9.880 -7.378 1.00 35.66 C
ATOM 7 SD MET A 0 24.111 10.428 -5.871 1.00 38.66 S
ATOM 8 CE MET A 0 24.454 12.221 -5.988 1.00 36.36 C
ATOM 9 N VAL A 1 25.922 11.435 -10.891 1.00 30.10 N
ATOM 10 CA VAL A 1 27.161 12.020 -11.393 1.00 27.92 C
ATOM 11 C VAL A 1 27.260 13.522 -11.114 1.00 26.21 C
ATOM 12 O VAL A 1 26.304 14.278 -11.304 1.00 26.54 O
ATOM 13 CB VAL A 1 27.312 11.769 -12.919 1.00 27.99 C
ATOM 14 CG1 VAL A 1 28.557 12.455 -13.466 1.00 27.68 C
ATOM 15 CG2 VAL A 1 27.395 10.282 -13.189 1.00 28.05 C
您可能会注意到,有些字段是空的。例如,第 17 列的 altLoc(alternative location) 是可选的,而第 79-80 列的费用通常会丢失。
有时字段没有分开,因为名称、altLoc 和 resName 字段可能形成类似 CG1AVAL 的东西,实际上是 CG1、A 和 VAL。
我正在尝试用 C++ 实现该程序。我尝试了 operator>> 和 fscanf 但未能找到将输入读入 struct Atom 的解决方案。
struct Atom
{
std::string recordName{ 6 };
int serial;
std::string name{ 4 };
char altLoc;
std::string resName{ 3 };
char chainID;
int resSeq;
char iCode;
double x;
double y;
double z;
double occupancy;
double tempFactor;
char segment;
char element;
char charge;
};
setw(size_t n) 无法按预期工作,并且由于我必须使用 41.9GB 的输入进行双重处理,因此性能很重要,因此我不希望像 getline() 那样在字符串中添加太多开销,然后解析它细绳。这是我失败的尝试:
ChainReader & ChainReader::operator>>(Atom & atom)
{
*stream //stream is a pointer to stream, which is a member of ChainReader
>> std::setw(4) >> atom.recordName
>> std::setw(7) >> atom.serial
>> std::setw(5) >> atom.name
>> std::setw(1) >> atom.altLoc
>> std::setw(3) >> atom.resName
>> std::setw(2) >> atom.chainID
>> std::setw(4) >> atom.resSeq
>> std::setw(1) >> atom.iCode
>> std::setw(11) >> atom.x
>> std::setw(8) >> atom.y
>> std::setw(8) >> atom.z
>> std::setw(6) >> atom.occupancy
>> std::setw(6) >> atom.tempFactor
>> std::setw(10) >> atom.segment
>> std::setw(2) >> atom.element
>> std::setw(2) >> atom.charge;
return *this;
}
更新:
想出一个使用 getline 和字符串解析的解决方案。工作,仍在测试性能。
ChainReader & ChainReader::operator>>(Atom & atom)
{
std::string line;
line.reserve(80);
std::getline(*stream, line);
atom.recordName = line.substr(0, 4);
atom.serial = std::stoi(line.substr(6, 5));
atom.name = line.substr(12, 4);
atom.altLoc = line[16];
atom.resName = line.substr(17, 3);
atom.chainID = line[21];
atom.resSeq = std::stoi(line.substr(22, 4));
atom.iCode = line[26];
atom.x = std::stod(line.substr(30, 8));
atom.y = std::stod(line.substr(38, 8));
atom.z = std::stod(line.substr(46, 8));
atom.occupancy = std::stod(line.substr(54, 6));
atom.tempFactor = std::stod(line.substr(60, 6));
atom.segment = line[75];
atom.element = line[77];
atom.charge = line[79];
return *this;
}
更新 2: 程序读取 ASTRAL 数据集中的 276231 条链并计算沿链的二面角。总共需要 10237134 毫秒,即 3 小时。数据集约42GB,性能完全可以接受。