【问题标题】:How to extract specific values in a string如何提取字符串中的特定值
【发布时间】:2019-07-27 08:41:38
【问题描述】:

我需要从文件输入中提取存储的字符串中的特定值。它有多个分隔符,我不知道如何从中提取每个特定值。

#include <vector>
#include <string>
#include <sstream>
#include <iostream>    
#include <fstream> 
using namespace std;


string ss = "[4, 90]-3-name";
// i need to extract the values 4, 90, 3 and name 
// the numbers can have multiple digits

stringstream tr(ss);
vector<string> result;

while( tr.good() )
{
    string substr;
    getline( ss, substr, '-' );
    result.push_back( substr );

}

for (int i = 0; i< result.size();i++)
    cout << result[i]<< endl;

output:
[4, 90]
3
name

【问题讨论】:

  • 可能重复? SO: How to extract specific elements from a string?(猜猜我是怎么找到这个的...);-)
  • C++ 有一个非常好的&lt;regex&gt; 库。
  • @Yksisarvinen 这是见仁见智的问题。我会这样描述它的不足。
  • 好吧,我的字符串有更多不同的分隔符。
  • 你对输入格式了解多少?

标签: c++ string delimiter


【解决方案1】:

如果您知道所有可能的分隔符,那么您可以将 ss 中的每个分隔符替换为连字符,然后您的上述代码就可以工作了。请参阅替换功能的链接 http://www.cplusplus.com/reference/string/string/replace/

【讨论】:

    【解决方案2】:

    Paul 的回答很聪明,但字符串可能是只读的。这是一个不需要修改字符串的版本

    int main()
    {
        string ss = "[4, 90]-3-name"; // i need to extract the values 4, 90, 3     and name
        vector<string> results;
        size_t size = ss.size();
        size_t first = 0;
        size_t i = 0;
        while (i < size)
        {
            char ch = ss[i];
            if (ch == '[' || ch == ']' || ch == ' ' || ch == '-' || ch == ',') // delimiter check
            {
                if (i > first)
                    results.push_back(ss.substr(first, i - first));
                first = i + 1;
            }
            ++i;
        }
        if (i > first)
            results.push_back(ss.substr(first, i - first));
        for (auto s : results)
            cout << s << '\n';
        return 0;
    }
    

    希望这是相当清楚的。诀窍是first 变量,它跟踪我们希望成为下一个要提取的值的第一个字符的字符的索引(即超出我们刚刚找到的分隔符的字符)。 if (i &gt; first) 检查只是确保我们不会在结果中添加任何零长度字符串。

    【讨论】:

      【解决方案3】:

      现在是 C++ 方法。这是使用面向对象的习语和现代 C++ 算法。

      我们有数据和方法,它们以某种方式属于一起。为此,C++ 中有类(结构)。因此,您可以定义一个具有成员变量和方法的类,该类可以与类变量一起使用。一切都作为一个对象工作。

      另外。该类知道如何读取或打印其值。只有班级应该知道这一点。这种智慧被封装了。

      接下来,我们要搜索嵌入在字符串某处的有趣数据。该字符串始终包含某种模式。在您的情况下,您有 3 个整数和一个字符串作为有趣的数据,以及介于两者之间的一些分隔符,无论它们是什么。

      为了匹配这些模式并搜索字符串中有趣的部分,C++ 提供了std::regex。它们非常强大,因此定义起来有点复杂。

      在下面的示例中,我将使用const std::regex re(R"((\d+).*?(\d+).*?(\d+).*?([\w_]+))");。这定义了 4 组子匹配(在括号中)和介于两者之间的东西。所以任何分隔符、空格或任何可能的东西。

      如果您想更严格,只需更改模式即可检测源数据中的错误。见const std::regex re(R"(\[(\d+)\,\ (\d+)\]\-(\d+)\-([\w_]+))");。这是一种更严格的方法。如果出现错误,将不会读取输入文件。或仅以有效数据开头。

      请看下面的例子:

      #include <string>
      #include <regex>
      #include <iterator>
      #include <iostream>
      #include <sstream>
      #include <fstream>
      #include <vector>
      #include <algorithm>
      #include <ios>
      #include <iomanip>
      
      std::istringstream testFile{ R"([1, 1]-3-Big_City
        [1, 2] - 3 - Big_City
        [1, 3] - 3 - Big_City
        [2, 1] - 3 - Big_City
        [2, 2] - 3 - Big_City
        [2, 3] - 3 - Big_City
        [2, 7] - 2 - Mid_City
        [2, 8] - 2 - Mid_City
        [3, 1] - 3 - Big_City
        [3, 2] - 3 - Big_City
        [3, 3] - 3 - Big_City
        [3, 7] - 2 - Mid_City
        [3, 8] - 2 - Mid_City
        [7, 7] - 1 - Small_City)" };
      
      
      
      const std::regex re(R"((\d+).*?(\d+).*?(\d+).*?([\w_]+))");
      
      
      struct CityData
      {
          // Define the city's data
          int xCoordinate{};
          int yCoordinate{};
          int cityId{};
          std::string cityName{};
      
          // Overload the extractor operator >> to read and parse a line
          friend std::istream& operator >> (std::istream& is, CityData& cd) {
      
              // We will read the line in this variable
              std::string line{};
      
              // Read the line and check, if it is OK
              if (std::getline(is, line)) {
      
                  // Find the matched substrings
                  std::smatch sm{};
                  if (std::regex_search(line, sm, re)) {
                      // An convert them to students record
                      cd.xCoordinate = std::stoi(sm[1]);
                      cd.yCoordinate = std::stoi(sm[2]);
                      cd.cityId = std::stoi(sm[3]);
                      cd.cityName = sm[3];
                  }
                  else {
                      is.setstate(std::ios::failbit);
                  }
              }
              return is;
          }
      
          friend std::ostream& operator << (std::ostream& os, const CityData& cd) {
              return os << cd.xCoordinate << ' ' << cd.yCoordinate << ' ' << cd.cityId;
          }
      };
      
      constexpr int MinimumArrayDimension = 8;
      
      int main()
      {
          // Define the variable cityData with the vectors range constructor. Read complete input file and parse data
          std::vector<CityData> cityData{ std::istream_iterator<CityData>(testFile),std::istream_iterator<CityData>() };
      
          // The following we are doing, because we want to print everything with the correct width
          // Read the maximum x coordinate
          const int maxRow = std::max(std::max_element (
              cityData.begin(), 
              cityData.end(), 
              [](const CityData & cd1, const CityData & cd2) { return cd1.xCoordinate < cd2.xCoordinate; }
          )->xCoordinate, MinimumArrayDimension);
      
          // Read the maximum y coordinate
          const unsigned int maxColumn = std::max(std::max_element(
              cityData.begin(),
              cityData.end(),
              [](const CityData & cd1, const CityData & cd2) { return cd1.yCoordinate < cd2.yCoordinate; }
          )-> yCoordinate, MinimumArrayDimension);
      
          // Read the maximum city
          const unsigned int maxCityID = std::max_element(
              cityData.begin(),
              cityData.end(),
              [](const CityData & cd1, const CityData & cd2) { return cd1.cityId < cd2.cityId; }
          )->cityId;
      
          // Get the number of digits that we have here
          const int digitSizeForRowNumber = maxRow > 0 ? (int)log10((double)maxRow) + 1 : 1;
      
          const int digitSizeForColumnNumber = std::max(maxColumn > 0 ? (int)log10((double)maxColumn) + 1 : 1,
                                                        maxCityID > 0 ? (int)log10((double)maxCityID) + 1 : 1);
      
          // Lambda function for printing the header and the footer
          auto printHeaderFooter = [&]() {
              std::cout << std::setw(digitSizeForColumnNumber) << "" << " #";
              for (int i = 0; i <= (maxColumn+1)* (digitSizeForColumnNumber+1); ++i)
                  std::cout << '#';
              std::cout << "#\n";
          };
      
      
          // Print the complete map
          std::cout << "\n\n";
          printHeaderFooter();
      
          // Print all rows
          for (int row = maxRow; row >= 0; --row) {
      
              // Ptint the row number at the beginning of the line
              std::cout << std::setw(digitSizeForColumnNumber) << row << " # ";
      
              // Print all columns
              for (int col = 0; col <= maxColumn; ++col)
              {
                  // Find the City ID for the given row (y) and column (x)
                  std::vector<CityData>::iterator cdi = std::find_if(
                      cityData.begin(),
                      cityData.end(),
                      [row, col](const CityData & cd) { return cd.yCoordinate == row && cd.xCoordinate == col; }
                  );
                  // If we could find nothing
                  if (cdi == cityData.end()) {
                      // Print empty space
                      std::cout << std::setw(digitSizeForColumnNumber) << "" << ' ';
                  }
                  else {
                      // Print the CityID
                      std::cout << std::right << std::setw(digitSizeForColumnNumber) << cdi->cityId << ' ';
                  }
              }
              // Print the end of the line
              std::cout <<  "#\n";
          }
          printHeaderFooter();
          // Print the column numbers
          std::cout << std::setw(digitSizeForColumnNumber) << "" << "   ";
          for (int col = 0; col <= maxColumn; ++col)
              std::cout << std::right << std::setw(digitSizeForColumnNumber) << col << ' ' ;
          // And, end
          std::cout << "\n\n\n";
      
          return 0;
      }
      
      

      请注意:main 读取文件并显示输出。

      而且,因为我不能在 SO 上使用文件,所以我从“std::istringstream”读取数据。这和从文件中读取是一样的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-20
        • 2019-02-19
        • 1970-01-01
        相关资源
        最近更新 更多