regexp.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:11k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: regexp.cpp,v $
  4.  * PRODUCTION Revision 1000.2  2004/06/01 19:40:23  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.7
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: regexp.cpp,v 1000.2 2004/06/01 19:40:23 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Author: Clifford Clausen
  35.  * File Description:
  36.  *         C++ wrappers for Perl Compatible Regular Expression (pcre) library
  37.  *
  38.  * ===========================================================================
  39.  */
  40. #include <ncbi_pch.hpp>
  41. #include <corelib/ncbi_limits.h>
  42. #include <util/regexp.hpp>
  43. #include <memory>
  44. #include <stdlib.h>
  45. BEGIN_NCBI_SCOPE
  46. //////////////////////////////////////////////////////////////////////////////
  47. //
  48. //  CRegexpException
  49. //
  50. class CRegexpException : public CException
  51. {
  52. public:
  53.     enum EErrCode {
  54.         eCompile
  55.     };
  56.     virtual const char* GetErrCodeString(void) const {
  57.         switch ( GetErrCode() ) {
  58.         case eCompile:         return "eCompile";
  59.         default:               return CException::GetErrCodeString();
  60.         }
  61.     }
  62.     NCBI_EXCEPTION_DEFAULT(CRegexpException,CException);
  63. };
  64. //////////////////////////////////////////////////////////////////////////////
  65. //
  66. //  CRegexp
  67. //
  68. CRegexp::CRegexp(const string& pattern, TCompile flags)
  69.     : m_NumFound(0)
  70. {
  71.     const char *err;
  72.     int err_offset;
  73.     m_PReg = pcre_compile(pattern.c_str(), flags, &err, &err_offset, NULL);
  74.     if (m_PReg == NULL) {
  75.         NCBI_THROW(CRegexpException, eCompile, "Compilation of the pattern '" +
  76.                    pattern + "' failed: " + err);
  77.     }
  78. }
  79. CRegexp::~CRegexp()
  80. {
  81.     (*pcre_free)(m_PReg);
  82. }
  83. void CRegexp::Set(const string& pattern, TCompile flags)
  84. {
  85.     if (m_PReg != NULL) {
  86.         (*pcre_free)(m_PReg);
  87.     }
  88.     const char *err;
  89.     int err_offset;
  90.     m_PReg = pcre_compile(pattern.c_str(), flags, &err, &err_offset, NULL);
  91.     if (m_PReg == NULL) {
  92.         NCBI_THROW(CRegexpException, eCompile, "Compilation of the pattern '" +
  93.                    pattern + "' failed: " + err);
  94.     }
  95. }
  96. string CRegexp::GetSub(const string& str, size_t idx) const
  97. {
  98.     int start = m_Results[2 * idx];
  99.     int end   = m_Results[2 * idx + 1];
  100.     if ((int)idx >= m_NumFound  ||  start == -1  ||  end == -1) {
  101.         return kEmptyStr;
  102.     }
  103.     return str.substr(start, end - start);
  104. }
  105. string CRegexp::GetMatch(
  106.     const string& str,
  107.     TSeqPos       offset,
  108.     size_t        idx,
  109.     TMatch        flags,
  110.     bool          noreturn)
  111. {
  112.     m_NumFound = pcre_exec(m_PReg, NULL, str.c_str(), (int)str.length(),
  113.                            (int)offset, flags, m_Results,
  114.                            (int)(kRegexpMaxSubPatterns +1) * 3);
  115.     if ( noreturn ) {
  116.         return kEmptyStr;
  117.     } else {
  118.         return GetSub(str, idx);
  119.     }
  120. }
  121. //////////////////////////////////////////////////////////////////////////////
  122. //
  123. //  CRegexpUtil
  124. //
  125. CRegexpUtil::CRegexpUtil(const string& str) 
  126.     : m_Content(str), m_IsDivided(false),
  127.       m_RangeStart(kEmptyStr), m_RangeEnd(kEmptyStr), m_Delimiter("n")
  128. {
  129.     return;
  130. }
  131. void CRegexpUtil::SetRange(
  132.         const string& addr_start,
  133.         const string& addr_end,
  134.         const string& delimiter)
  135. {
  136.     m_RangeStart = addr_start;
  137.     m_RangeEnd   = addr_end;
  138.     x_Divide(delimiter);
  139. }
  140. size_t CRegexpUtil::Replace(
  141.     const string&     search,
  142.     const string&     replace,
  143.     CRegexp::TCompile compile_flags,
  144.     CRegexp::TMatch   match_flags,
  145.     size_t            max_replace)
  146. {
  147.     if ( search.empty() ) {
  148.         return 0;
  149.     }
  150.     size_t n_replace = 0;
  151.     // Fill shure that string is not divided.
  152.     x_Join();
  153.     // Compile regular expression.
  154.     CRegexp re(search, compile_flags);
  155.     size_t  start_pos = 0;
  156.     for (size_t count = 0; !(max_replace && count >= max_replace); count++) {
  157.         // Match pattern.
  158.         re.GetMatch(m_Content.c_str(), (int)start_pos, 0, match_flags, true);
  159.         int num_found = re.NumFound();
  160.         if (num_found <= 0) {
  161.             break;
  162.         }
  163.         // Substitute all subpatterns "$<digit>" to values in the "replace"
  164.         // string.
  165.         const int* result;
  166.         string     x_replace = replace;
  167.         size_t     pos = 0;
  168.         for (;;) {
  169.             // Find "$"
  170.             pos = x_replace.find("$", pos);
  171.             if (pos == NPOS) {
  172.                 break;
  173.             }
  174.             // Try to convert string after the "$" to number
  175.             errno = 0;
  176.             const char* startptr = x_replace.c_str() + pos + 1;
  177.             char* endptr = 0;
  178.             long value = strtol(startptr, &endptr, 10);
  179.             if ( errno  ||  endptr == startptr  ||  !endptr  ||
  180.                  value < kMin_Int  ||  value > kMax_Int) {
  181.                 // Format error, skip single "$".
  182.                 pos++;
  183.                 continue;
  184.             }
  185.             int n = (int)value;
  186.             // Get subpattern value
  187.             string subpattern;
  188.             if ( n > 0  &&  n < num_found ) {
  189.                 result = re.GetResults(n);
  190.                 if (result[0] >= 0  &&  result[1] >= 0) {
  191.                     subpattern = m_Content.substr(result[0],
  192.                                                   result[1] - result[0]);
  193.                 }
  194.             }
  195.             // Check braces {$...}
  196.             size_t sp_start = pos;
  197.             size_t sp_end   = endptr - x_replace.c_str();
  198.             if ( sp_start > 0  &&  x_replace[sp_start-1] == '{') {
  199.                 sp_start--;
  200.                 if ( sp_end <  x_replace.length()  &&
  201.                      x_replace[sp_end] == '}') {
  202.                     sp_end++;
  203.                 } else {
  204.                     // Format error -- missed closed brace.
  205.                     sp_start++;
  206.                 }
  207.             }
  208.             // Replace $n with subpattern value.
  209.             x_replace.replace(sp_start, sp_end - sp_start, subpattern);
  210.             pos += subpattern.length();
  211.         }
  212.         // Replace pattern with "x_replace".
  213.         result = re.GetResults(0);
  214.         m_Content.replace(result[0], result[1] - result[0], x_replace);
  215.         n_replace++;
  216.         start_pos = result[0] + x_replace.length();
  217.     }
  218.     return n_replace;
  219. }
  220. size_t CRegexpUtil::ReplaceRange(
  221.     const string&       search,
  222.     const string&       replace,
  223.     CRegexp::TCompile   compile_flags,
  224.     CRegexp::TMatch     match_flags,
  225.     CRegexpUtil::ERange process_inside,
  226.     size_t              max_replace
  227.     )
  228. {
  229.     if ( search.empty() ) {
  230.         return 0;
  231.     }
  232.     size_t n_replace = 0;
  233.     // Split source string to parts by delimiter
  234.     x_Divide();
  235.     // Flag which denote that current line is inside "range"
  236.     bool inside = m_RangeStart.empty();
  237.     NON_CONST_ITERATE (list<string>, i, m_ContentList) {
  238.         // Get new line
  239.         string line = *i;
  240.         // Check beginning of block [addr_re_start:addr_re_end]
  241.         if ( !inside  &&  !m_RangeStart.empty() ) {
  242.             CRegexp re(m_RangeStart.c_str());
  243.             re.GetMatch(line.c_str(), 0, 0, 0, true);
  244.             inside = (re.NumFound() > 0);
  245.         } else {
  246.             inside = true;
  247.         }
  248.         // Process current line
  249.         if ( (inside  &&  process_inside == eInside)  ||
  250.              (!inside  &&  process_inside == eOutside) ) {
  251.             CRegexpUtil re(line);
  252.             n_replace += re.Replace(search, replace,
  253.                                     compile_flags, match_flags, max_replace);
  254.             *i = re;
  255.         }
  256.         // Check ending of block [addr_re_start:addr_re_end]
  257.         if ( inside  &&  !m_RangeEnd.empty() ) {
  258.             // Two addresses
  259.             CRegexp re(m_RangeEnd.c_str());
  260.             re.GetMatch(line.c_str(), 0, 0, 0, true);
  261.             inside = (re.NumFound() <= 0);
  262.         } else {
  263.             // One address -- process one current string only
  264.             inside = false;
  265.         }
  266.     }
  267.     return n_replace;
  268. }
  269. void CRegexpUtil::x_Divide(const string& delimiter)
  270. {
  271.     string x_delimiter = delimiter.empty() ? m_Delimiter : delimiter;
  272.     if ( m_IsDivided  ) {
  273.         if ( x_delimiter == m_Delimiter ) {
  274.             return;
  275.         }
  276.         x_Join();
  277.     }
  278.     m_ContentList.clear();
  279.     // Split source string to parts by delimiter
  280.     size_t pos;
  281.     size_t start_pos = 0;
  282.     for (;;) {
  283.         pos = m_Content.find(x_delimiter, start_pos);
  284.         if (pos == NPOS) {
  285.             m_ContentList.push_back(m_Content.substr(start_pos));
  286.             break;
  287.         } else {
  288.             m_ContentList.push_back(m_Content.substr(start_pos,
  289.                                                      pos - start_pos));
  290.             start_pos = pos + x_delimiter.length();
  291.         }
  292.     }
  293.     m_IsDivided = true;
  294.     // Save delimiter for consecutive joining
  295.     m_Delimiter = x_delimiter;
  296. }
  297. void CRegexpUtil::x_Join(void)
  298. {
  299.     if ( m_IsDivided ) {
  300.         m_Content = NStr::Join(m_ContentList, m_Delimiter);
  301.         m_IsDivided = false;
  302.     }
  303. }
  304. END_NCBI_SCOPE
  305. /*
  306.  * ===========================================================================
  307.  * $Log: regexp.cpp,v $
  308.  * Revision 1000.2  2004/06/01 19:40:23  gouriano
  309.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.7
  310.  *
  311.  * Revision 1.7  2004/05/17 21:06:02  gorelenk
  312.  * Added include of PCH ncbi_pch.hpp
  313.  *
  314.  * Revision 1.6  2003/11/07 17:16:23  ivanov
  315.  * Fixed  warnings on 64-bit Workshop compiler
  316.  *
  317.  * Revision 1.5  2003/11/07 13:39:56  ivanov
  318.  * Fixed lines wrapped at 79th columns
  319.  *
  320.  * Revision 1.4  2003/11/06 16:13:04  ivanov
  321.  * Added CRegexpUtil class. Some formal code rearrangement.
  322.  *
  323.  * Revision 1.3  2003/07/16 19:13:50  clausen
  324.  * Added TCompile and TMatch
  325.  *
  326.  * Revision 1.2  2003/06/20 18:26:37  clausen
  327.  * Switched to native regexp interface
  328.  *
  329.  * Revision 1.1  2003/06/03 14:46:23  clausen
  330.  * Initial version
  331.  *
  332.  * ===========================================================================
  333.  */