2013-07-19 70 views
0

我搜索最簡潔高效的方式來查找C++字符串中的第一個printf格式序列(轉換規範)(我不能使用std::regex,因爲它們尚未在大多數情況下實現編譯器)。查找C++字符串中的第一個printf格式序列

所以,問題是寫一個優化的函數,將來自輸入字符串str返回第printf -format序列pos的開始和其長度n

inline void detect(const std::string& str, int& pos, int& n); 

例如,對於:

  • %d- >pos = 0n = 2
  • the answer is: %05d- >pos = 15n = 4
  • the answer is: %% %4.2f haha- >pos = 18n = 5

如何做到這一點(聰明和狡猾的方式,歡迎)?

+1

爲什麼不只是抓住一個開源的'printf'實現,並將解析器位從其中解壓出來? –

+0

你不需要正則表達式。 printf格式說明符格式可以從左到右一次一個字符地解析。 –

+0

如果您查看完整的['printf()'](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html)格式的POSIX規範,則會出現很多可能出現的字符在格式規範中。例如,'%100 $#+ - 0'* 101 $。* 102 $ llX'可能是'有效'的,儘管標誌的某些組合沒有意義。 –

回答

0

向前掃描%,然後從那裏解析內容。有一些古怪的,但不是那麼糟糕(不知道你想把它做成inline壽)。一般原則(我只是打字,因爲我可能不是最好的代碼形式 - 我沒有試圖編譯它)。

inline void detect(const std::string& str, int& pos, int& n) 
{ 
    std::string::size_type last_pos = 0; 
    for(;;) 
    { 
     last_pos = str.find('%', last_pos) 
     if (last_pos == std::string::npos) 
      break; // Not found anythin. 
     if (last_pos == str.length()-1) 
      break;  // Found stray '%' at the end of the string. 
     char ch = str[last_pos+1]; 

     if (ch == '%') // double percent -> escaped %. Go on for next. 
     { 
      last_pos += 2; 
      continue; 
     } 
     pos = last_pos; 
     do 
     { 
      if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' || 
       ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' || 
       ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' || 
       ch == '#' || ch == '\'') 
      { 
       last_pos++; 
       ch = str[last_pos+1]; 
      } 
      else 
      { 
       // The below string may need appending to depending on version 
       // of printf. 
       if (string("AacdeEfFgGiopusxX").find(ch) != std::string::npos) 
       { 
        // Do something about invalid string? 
       } 
       n = last_pos - pos; 
       return; 
       } 
     } while (last_pos < str.length()); 
    } 
} 

EDIT2:該位可能是更好的寫法如下:

   if (isdigit(ch)) || ch == '.' || ch == '-' || ch == '*' || 
       ch == '+' || ch == 'l' || ch == 'L' || ch == 'z' || 
       ch == 'h' || ch == 't' || ch == 'j' || ch == ' ' || 
       ch == '#' || ch == '\'') ... 

if (string(".-*+lLzhtj #'").find(ch) != std::string::npos) ... 

現在,那是你完成家庭作業。請回報你得到什麼等級。

編輯:應該指出的是,一些常規printf將「拒絕」的東西被上面的代碼所接受,例如, 「%....... 5 ...... 6f」,「%5.8d」,「%-5-6d」或「%----- 09 --- 5555555555555555llllld」。如果你想要代碼拒絕這些事情,這不是一個額外的工作量,只需要一點邏輯來檢查「我們看過這個字符之前」的「檢查特殊字符或數字」,並且在大多數情況下,只能允許一次特殊字符。正如評論所說,我可能錯過了一些有效的格式說明符。如果你還需要應對「這個''''不允許'c''或這樣的規則,它會變得更加棘手。但是,如果輸入不是「惡意的」(​​例如,你想註釋在哪一行上有格式說明符在工作的C源文件中),上述應該工作得很好。

+0

'h'是一個長度修飾符(像'L'和'l'),就像'j'和't'一樣。空格和'#'都是標誌;在POSIX 2008中,'''也是一個標誌。您似乎錯過了作爲轉換說明符的'A','a','E','F','g','G','i'。 'z'是長度修飾符而不是轉換說明符,所以需要在'z'後面加上轉換說明符。 POSIX支持'5 $'等來按位置指定參數。實際上驗證這些東西實際上是非常艱苦的工作,而不是接受可能合法的字符序列。是否有必要取決於你要做什麼。 –

+0

好吧,我只是沒有查看它,所以我想可能會更糟糕。我已經更新了您提到的額外內容,並且移動了'z'。是的,驗證完成確定似乎有點棘手,所以我只是決定「是否可能是一個有效的格式說明符」。 –

+0

我剛剛超過400行代碼(不包括註釋和測試程序),它將'printf()'格式的字符串解析爲結構,或將其中一個結構轉換爲格式字符串。我沒有表現出來,因爲它對於SO來說太長了。它是C代碼,而不是C++代碼。 –

相關問題