Как использовать istream для чтения форматированного ввода с пустым полем

Я хотел бы прочитать файл с указанным форматом с помощью std :: istream или fscanf ().

Каждая строка файла состоит из нескольких полей. Поля могут быть char, float или integer. Каждое поле имеет фиксированную ширину и может быть пустым. Есть ли способ остановить 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

Как вы можете заметить, некоторые поля пусты. Например, altLoc (альтернативное местоположение) в столбце 17 является необязательным, а плата в столбце 79-80 обычно отсутствует.

Иногда поля не разделяются, так как поля name, altLoc и resName могут образовывать что-то вроде CG1AVAL, который на самом деле является CG1, A и VAL.

Я пытаюсь реализовать программу с C ++. Я пробовал оба оператора >> и fscanf, но не смог найти решение для чтения ввода в структуру 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,9 ГБ ввода, производительность важна, поэтому я предпочитаю не добавлять слишком много накладных расходов, как 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 и string. Работает, все еще тестирование на производительность.

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:
Программа считывает 276231 цепочки в наборе данных ASTRAL и вычисляет двугранные углы вдоль цепочек. Занимает всего 10237134 мс или 3 часа. Набор данных составляет около 42 ГБ, поэтому производительность вполне приемлема.

1

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]