如何提取字符串中的特定值

Posted

技术标签:

【中文标题】如何提取字符串中的特定值【英文标题】: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 这是见仁见智的问题。我会这样描述它的不足。 好吧,我的字符串有更多不同的分隔符。 你对输入格式了解多少? 【参考方案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”读取数据。这和从文件中读取是一样的。

【讨论】:

以上是关于如何提取字符串中的特定值的主要内容,如果未能解决你的问题,请参考以下文章

如何从邮递员的响应标头中提取特定的字符串值?

R语言中如何提取字符串

使用 keyof 提取仅具有特定类型值的键的字符串文字联合

如何从 C++ 中的 getline 函数中提取特定的子字符串?

如何通过C#中的特定片段从句子中提取整个单词?

在C#中的某个特定字符后获取值