代码之家  ›  专栏  ›  技术社区  ›  Rick

如何加快大型字符串的解析速度?

  •  2
  • Rick  · 技术社区  · 6 年前

    所以我做了一个程序,可以读取各种配置文件。其中一些配置文件可以是小的,一些可以是半大的(最大的是3844KB)。

    我想知道是否有什么方法可以加快解析速度,或者是否有一个现有的库可以满足我的需要(提取字符串直到一个分隔符&在同一级别上的两个分隔符之间提取字符串)。任何帮助都会很好。

    这是我的代码&它应该如何工作的示例。。。

    #include "stdafx.h"
    
    #include <string>
    #include <vector>
    
    std::string ExtractStringUntilDelimiter(
       std::string& original_string,
       const std::string& delimiter,
       const int delimiters_to_skip = 1)
    {
       std::string needle = "";
    
       if (original_string.find(delimiter) != std::string::npos)
       {
          int total_found = 0;
    
          auto occurance_index = static_cast<size_t>(-1);
    
          while (total_found != delimiters_to_skip)
          {
             occurance_index = original_string.find(delimiter);
             if (occurance_index != std::string::npos)
             {
                needle = original_string.substr(0, occurance_index);
                total_found++;
             }
             else
             {
                break;
             }
          }
    
          // Remove the found string from the original string...
          original_string.erase(0, occurance_index + 1);
       }
       else
       {
          needle = original_string;
          original_string.clear();
       }
    
       if (!needle.empty() && needle[0] == '\"')
       {
          needle = needle.substr(1);
       }
       if (!needle.empty() && needle[needle.length() - 1] == '\"')
       {
          needle.pop_back();
       }
    
       return needle;
    }
    
    void ExtractInitialDelimiter(
       std::string& original_string,
       const char delimiter)
    {
       // Remove extra new line characters
       while (!original_string.empty() && original_string[0] == delimiter)
       {
          original_string.erase(0, 1);
       }
    }
    
    void ExtractInitialAndFinalDelimiters(
       std::string& original_string,
       const char delimiter)
    {
       ExtractInitialDelimiter(original_string, delimiter);
    
       while (!original_string.empty() && original_string[original_string.size() - 1] == delimiter)
       {
          original_string.erase(original_string.size() - 1, 1);
       }
    }
    
    std::string ExtractStringBetweenDelimiters(
       std::string& original_string,
       const std::string& opening_delimiter,
       const std::string& closing_delimiter)
    {
       const size_t first_delimiter = original_string.find(opening_delimiter);
       if (first_delimiter != std::string::npos)
       {
          int total_open = 1;
          const size_t opening_index = first_delimiter + opening_delimiter.size();
    
          for (size_t i = opening_index; i < original_string.size(); i++)
          {
             // Check if we have room for opening_delimiter...
             if (i + opening_delimiter.size() <= original_string.size())
             {
                for (size_t j = 0; j < opening_delimiter.size(); j++)
                {
                   if (original_string[i + j] != opening_delimiter[j])
                   {
                      break;
                   }
                   else if (j == opening_delimiter.size() - 1)
                   {
                      total_open++;
                   }
                }
             }
    
    
             // Check if we have room for closing_delimiter...
             if (i + closing_delimiter.size() <= original_string.size())
             {
                for (size_t j = 0; j < closing_delimiter.size(); j++)
                {
                   if (original_string[i + j] != closing_delimiter[j])
                   {
                      break;
                   }
                   else if (j == closing_delimiter.size() - 1)
                   {
                      total_open--;
                   }
                }
             }
    
    
             if (total_open == 0)
             {
                // Extract result, and return it...
                std::string needle = original_string.substr(opening_index, i - opening_index);
                original_string.erase(first_delimiter, i + closing_delimiter.size());
    
                // Remove new line symbols
                ExtractInitialAndFinalDelimiters(needle, '\n');
                ExtractInitialAndFinalDelimiters(original_string, '\n');
    
                return needle;
             }
          }
       }
    
       return "";
    }
    
    int main()
    {
       std::string sample = "{\n"
          "Line1\n"
          "Line2\n"
          "{\n"
             "SubLine1\n"
             "SubLine2\n"
          "}\n"
       "}";
    
       std::string result = ExtractStringBetweenDelimiters(sample, "{", "}");
       std::string LineOne = ExtractStringUntilDelimiter(result, "\n");
       std::string LineTwo = ExtractStringUntilDelimiter(result, "\n");
    
       std::string SerializedVector = ExtractStringBetweenDelimiters(result, "{", "}");
       std::string SubLineOne = ExtractStringUntilDelimiter(SerializedVector, "\n");
       std::string SubLineTwo = ExtractStringUntilDelimiter(SerializedVector, "\n");
    
       // Just for testing...
       printf("LineOne: %s\n", LineOne.c_str());
       printf("LineTwo: %s\n", LineTwo.c_str());
       printf("\tSubLineOne: %s\n", SubLineOne.c_str());
       printf("\tSubLineTwo: %s\n", SubLineTwo.c_str());
       system("pause");
    }
    
    3 回复  |  直到 6 年前
        1
  •  5
  •   Yakk - Adam Nevraumont    6 年前

    使用 string_view 或者是手卷的。

    不要修改加载的字符串。

      original_string.erase(0, occurance_index + 1);
    

    如果你要修改一些东西,一次就完成。不要重复删除它前面的内容——即O(n^2)。取而代之的是,继续进行,并将“完成”的内容放入一个输出累加器。

    这将涉及更改代码的工作方式。

        2
  •  1
  •   paulsm4    6 年前
    1. 你在用“字符串。查找().". 那不一定是 坏的 选择。

    2. 你在用“字符串。擦除()". 那可能是你问题的主要根源。

    建议:

    • 将原始字符串视为“只读”。不要调用erase(),不要修改它。

    • 就我个人而言,我会考虑将文本读入一个C字符串(文本缓冲区),然后使用 strstr() .

        3
  •  1
  •   Richard Hodges    6 年前

    这里有一个更有效的版本 ExtractStringBetweenDelimiters . 请注意,此版本不会改变原始缓冲区。您将对返回的字符串执行后续查询。

    std::string trim(std::string buffer, char what)
    {
        auto not_what = [&what](char ch)
        {
            return ch != what;
        };
        auto first = std::find_if(buffer.begin(), buffer.end(), not_what);
        auto last = std::find_if(buffer.rbegin(), std::make_reverse_iterator(first), not_what).base();
        return std::string(first, last);
    }
    
        std::string ExtractStringBetweenDelimiters(
            std::string const& buffer,
            const char opening_delimiter,
            const char closing_delimiter)
        {
            std::string result;
    
            auto first = std::find(buffer.begin(), buffer.end(), opening_delimiter);
            if (first != buffer.end())
            {
                auto last = std::find(buffer.rbegin(), std::make_reverse_iterator(first),
                                             closing_delimiter).base();
                if(last > first)
                {
                    result.assign(first + 1, last);
                    result = trim(std::move(result), '\n');
                }
            }
            return result;
        }
    

    string_view (对于std::string\u视图或boost::string\u视图,使用c++17)为了提高效率,您可以从这两个函数中返回其中一个。

    最后,您将需要编写或使用其他人的解析器。

    这个 boost::spirit