c++将文本附加到字符串中,直到看到特定的字符

c++ appending text into a string until see a specific character

本文关键字:字符 文本 字符串 c++      更新时间:2023-10-16

我有不止一个这样的输入文件:

>1aab_
GKGDPKKPRGKMSSYAFFVQTSREEHKKKHPDASVNFSEFSKKCSERWKT
MSAKEKGKFEDMAKADKARYEREMKTYIPPKGE
>1j46_A
MQDRVKRPMNAFIVWSRDQRRKMALENPRMRNSEISKQLGYQWKMLTEAE
KWPFFQEAQKLQAMHREKYPNYKYRPRRKAKMLPK
>1k99_A
MKKLKKHPDFPKKPLTPYFRFFMEKRAKYAKLHPEMSNLDLTKILSKKYK
ELPEKKKMKYIQDFQREKQEFERNLARFREDHPDLIQNAKK
>2lef_A
MHIKKPLNAFMLYMKEMRANVVAESTLKESAAINQILGRRWHALSREEQA
KYYELARKERQLHMQLYPGWSARDNYGKKKKRKREK

这里,我要做的:

vector <string> names;
vector <string> seqs;
names.resize(total); //"total" is already known.
seqs.resize(total);
counter=0;char input;
while ((input = myInput.get()) != EOF)
{
   if(input=='>')
      names[counter]= take all line (>1aab_, >1j46_A, so...)
   else
      untill the see next '>' append the character into sequence[counter]
   counter++;
}

最后是这样的:

names[0]=">1aab_"
sequence[0]="GKGDPKKPRGKMSSYAFFVQTSREEHKKKHPDASVNFSEFSKKCSERWKTMSAKEKGKFEDMAKADKARYEREMKTYIPPKGE"

等等。

我想了两个小时都想不通。有人能帮我吗?提前谢谢。

有几种方法可以解决这个问题;我会给出一些例子,但我不是在测试/编译这些代码,所以可能会有一些小错误——逻辑是重要的。

由于您的伪代码似乎是逐字符处理输入的,因此我将其视为一种要求。


你似乎认为它是用一对循环来实现的——一个用于读取名称,另一个用于阅读序列——它们被封装在一个外部循环中,以便处理所有记录。

这看起来像以下内容:

// first character in file should be a '>', indicating the start
// of a record.
input = myInput.get();
if (input != '>')
{
  std::cerr << "Malformed input file!" << std::endl;
  return /*...*/;
}
do
{
   // record name continues up until the newline
   while ((input = myInput.get()) != EOF)
   {
     if (input == 'n' || input == 'r')
       break;
     names[counter].push_back(input);
   }
   // read sequence until we hit a '>' or EOF
   while ((input = myInput.get()) != EOF)
   {
     if (input == '>')
     {
       // advance to next record number
       counter++;
       break;
     }
     sequence[counter].push_back(input);
   }
} while (input != EOF && counter < total);

您还会注意到,我将初始'>'的检查移到了循环之前,这是一种吸收(和丢弃)字符的方式,也是对输入进行基本健全性检查的方式。这是因为我们真的用这个字符来标记序列的结束(而不是"记录的开始")——当我们进入循环时,我们假设我们已经在读取记录名称。


另一种方法是使用状态机。本质上,这利用了额外的变量来跟踪解析器所处的状态

对于这种特殊情况,您只有两种状态:要么正在读取记录名,要么正在读取序列。所以,我们可以只使用一个布尔值来跟踪我们所处的状态

有了状态变量,我们就可以根据所处的状态来决定如何处理刚刚读取的字符。在最简单的级别上,如果我们处于"读取记录名称"状态,我们会将字符添加到names变量中,否则我们会将其添加到sequence变量中。

// state flag to indicate if we're currently reading a name line,
// i.e. a line starting with ">"
// This should be set true by the first record we encounter, so
// we'll set it false (to indicate we're reading a sequence) in
// order to allow us to detect bad input files.
bool reading_name = false;
// indicate we're on the first record, so we can avoid incrementing
// the record counter
bool first_record = true;
// process input character-by-character until end of file
while ((input = myInput.get()) != EOF)
{
   // check for start of new record
   if (input == '>')
   {
     // for robustness, verify we're not already reading a name,
     // as this probably indicates invalid input
     if (reading_name)
     {
       std::cerr << "Input is malformed?!" << endl;
       break;
     }
     // switch to reading name state
     reading_name = true;
     // advance to next record, but only if it isn't the first record
     if (first_record)
     {
       // disable the first_record flag, and explicitly set the
       // record counter to 0.
       first_record = false;
       counter = 0;
     }
     else if (++counter >= total)
     {
       std::cerr << "Error: too many records!" << std::endl;
       break;
     }
   }
   // first character in file should start a new record
   else if (first_record)
   {
     std::cerr << "Missing record start character at beginning of input!" << std::endl;
     break;
   }
   // make sure we are processing a valid record number
   else if (counter >= total)
   {
     std::cerr << "Invalid record number!" << std::endl;
     break;
   }
   // continue reading the name
   else if (reading_name)
   {
      // check if we've reached the end of the line; you
      // may also want/need to check for r if your input
      // files may have Windows-style line endings
      if (input == 'n')
      {
        // switch to reading sequence state
        reading_name = false;
      }
      else
      {
        // add character to current name
        names[counter].push_back(input);
      }
   }
   // continue reading the sequence
   else
   {
      // you might need to handle line ending characters here,
      // maybe just skipping them?
      // add character to current sequence
      sequence[counter].push_back(input);
   }
}

这增加了相当多的复杂性,这对这项特定的工作来说有值得怀疑的价值,但确实使未来添加更多的州变得更容易。它还有一个好处,即在代码中只有一个位置可以完成I/O,这减少了出错的机会(不检查EOF、溢出数组边界等)

在这种情况下,我们实际上使用'>'字符作为新记录正在启动的指示符,因此我们添加了一些额外的逻辑,以确保所有内容都能与记录计数器一起正常工作。您也可以为counter变量使用一个带符号的整数,并从-1开始,这样它在第一条记录开始时会增加到0,但使用带符号的变量对数组进行索引不是一个好主意。


有更多的方法可以解决这个问题,但希望这能给你一个开始自己解决问题的地方。