1

我一次解析一个字符的流,如下面高度简化的示例所示。我的问题是我需要将 转换\\n为实际的换行符(对于任何其他转义字符也是如此)。

有没有比我在这里做的手动方式更好的方法?因为如果我必须以这种方式转换每个可能的转义字符,那将变得相当麻烦。

char c ;
std::stringstream s("foo \\n bar") ;

while (s.good()) {
    c = s.get() ;
    if (!s.good()) break ;
    if (c == '\\' && s.get() == 'n') c = '\n' ;
    std::cout << c ;
}
4

4 回答 4

4

像这样的东西:

int c;     // Not char.
while((c = s.get() != EOF)
{
    if (c == '\\')
    {
        switch(int escaped = s.get())
        {
           case 't':
              c = '\t';
              break;
           case 'n':
              c = '\n';
              break;
           case 'a':
              c = '\a';
           ...
           default:
              std::cout << c << escaped;    // Retain "invalid variants". 
              c = 0; 
              break;
        }
    }
    if (c)
    {
        std::cout << c;
    }
}

请注意,如果您想要一个完整的解决方案,您还需要处理\033,\x1b\u0417(分别为八进制或十六进制的随机不可打印字符和 unicode 字符)

于 2013-09-09T00:04:37.310 回答
3

您基本上是在编写一个简单的词法分析器;手动执行此操作的常用方法是使用状态机。

#include <iostream>
#include <stdexcept>
#include <sstream>
using namespace std;

int main() {
  char c;
  istringstream s("foo \\n bar");
  enum { CHARACTER, ESCAPE } state = CHARACTER;

  while (s.get(c)) {    
    switch (state) {
    case CHARACTER:
      if (c == '\\') {
        state = ESCAPE;
      } else {
        cout << c;
      }
      break;

    case ESCAPE:
      switch (c) {
      case 'n': cout << '\n'; break;
      case 't': cout << '\t'; break;
      default:
        throw runtime_error("unknown escape");
      }
      state = CHARACTER;
      break;
    }
  }
}

当然,对于这个简单的例子,转义表可以是 a map<char, char>,但如果你想支持更多奇特的转义,例如十六进制数字\xNN在哪里NN,那么概括一下是值得的。

状态机方法的优点是有一个读取字符的位置。各个状态只负责将该字符添加到他们认为合适的输出中,并通过分配state变量来转换到其他状态。

于 2013-09-09T00:06:16.400 回答
1

您的代码在很大程度上是正确的,尽管我一点也不觉得它特别麻烦。

但是,您的代码有一个错误:如果您将其作为输入:'\\', 'g'那么g' character will be lost because you don't preserve the data retrieved by your seconds.get()` 调用。

这就是我的做法:

bool escaped = false;
while (s.good()) {
    c = s.get() ;
    if (!s.good()) break ;
    if( escaped ) {
        if( c == 'n' ) {
            std::cout << '\n' ;
        } else {
            std::cout << '\\' << c;
        }
        escaped = false;
    } else {
        if( c == '\\' ) {
            escaped = true;
        } else {
            std::cout << c ;
        }
    }
}
于 2013-09-09T00:02:48.717 回答
0

我不建议将它按原样用于 C++,但这是我大约 20 年前用 C 编写的一些代码,可能会提供一些启发。

#include <string.h>
#include <stdio.h>

char *translate(char *string)
{
      char *here=string;
      size_t len=strlen(string);
      int num;
      int numlen;

      while (NULL!=(here=strchr(here,'\\')))
      {
            numlen=1;
            switch (here[1])
            {
            case '\\':
                  break;

            case 'r':
                  *here = '\r';
                  break;

            case 'n':
                  *here = '\n';
                  break;

            case 't':
                  *here = '\t';
                  break;

            case 'v':
                  *here = '\v';
                  break;

            case 'a':
                  *here = '\a';
                  break;

            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
                  numlen = sscanf(here,"%o",&num);
                  *here = (char)num;
                  break;

            case 'x':
                  numlen = sscanf(here,"%x",&num);
                  *here = (char) num;
                  break;
            }
            num = here - string + numlen;
            here++;
            memmove(here,here+numlen,len-num );
      }
      return string;
}
于 2013-09-09T00:07:49.197 回答