这似乎微不足道,但我似乎无法解决这个问题。我有格式2013 336 (02 DEC) 04
的STL字符串(其中04
是小时,但那是无关紧要的)。我想提取月中的某一天(示例中为02
)以及月份和小时。
我正试图干净利落地避免在括号中拆分字符串,然后使用子字符串等。理想情况下,我想使用stringstream
并将其重定向到变量。我现在得到的代码是:
int year, dayOfYear, day;
std::string month, leftParenthesis, rightParenthesis;
std::string ExampleString = "2013 336 (02 DEC) 04";
std::istringstream yearDayMonthHourStringStream( ExampleString );
yearDayMonthHourStringStream >> year >> dayOfYear >> leftParenthesis >> day >> month >> rightParenthesis >> hour;
它将year
和dayOfYear
提取为2013
和336
,但事情开始变得糟糕。 day
为0
,month
为空字符串,hour
为843076624。
leftParenthesis
是(02
所以它包含day
,但是当我尝试省略leftParenthesis
变量时重定向yearDayMonthHourStringStream
流day
是还0
。
有关如何处理此事的任何想法?我不知道正则表达式(但是),并且不可否认,我不确定我是否能够立即学习(时间)。
修改 好的,我知道了。虽然这就像我能用正则表达式让生活变得那么容易的第十亿次,所以我想是时候了。无论如何,有效的是:
int year, dayOfYear, day, month, hour, minute, revolution;
std::string dayString, monthString;
yearDayMonthHourStringStream >> year >> dayOfYear >> dayString >> monthString >> hour;
std::string::size_type sz;
day = std::stod( dayString.substr( dayString.find("(")+1 ), &sz ); // Convert day to a number using C++11 standard. Ignore the ( that may be at the beginning.
这仍然需要处理monthString
,但我还是需要将其更改为数字,因此这不是一个巨大的劣势。不是你能做的最好的事情(正则表达式)但是工作并且不是太脏。据我所知,也是模糊的便携式,希望不会停止使用新的编译器。但是,谢谢大家。
答案 0 :(得分:7)
显而易见的解决方案 使用正则表达式(或者
{C} 11中的std::regex
或C ++ 11中的boost::regex
。只是
捕获您感兴趣和使用的群组
如果需要,std::istringstream
可以转换它们。在这
情况下,
std::regex re( "\\s*\\d+\\s+\\d+\\s*\\((\\d+)\\s+([[:alpha:]]+))\\s*(\\d+)" );
应该做的伎俩。
正则表达式非常简单;它需要 你学习它们的时间少于实施任何替代方案 溶液
对于替代解决方案,您可能希望阅读 按字符排列,将其分解为标记。某物 沿着这条线:
std::vector<std::string> tokens;
std::string currentToken;
char ch;
while ( source.get(ch) && ch != '\n' ) {
if ( std::isspace( static_cast<unsigned char>( ch ) ) ) {
if ( !currentToken.empty() ) {
tokens.push_back( currentToken );
currentToken = "";
}
} else if ( std::ispunct( static_cast<unsigned char>( ch ) ) ) {
if ( !currentToken.empty() ) {
tokens.push_back( currentToken );
currentToken = "";
}
currentToken.push_back( ch );
} else if ( std::isalnum( static_cast<unsigned char>( ch ) ) ) {
currentToken.push_back( ch );
} else {
// Error: illegal character in line. You'll probably
// want to throw an exception.
}
}
if ( !currentToken.empty() ) {
tokens.push_back( currentToken );
}
在这种情况下,一系列字母数字字符是一个标记, 就像任何一个标点字符一样。你可以走得更远, 确保令牌是全部字母或全部数字,以及 也许重新组合标点符号序列,但这似乎是 足以解决你的问题了。
一旦获得了令牌列表,您就可以做任何必要的事情 验证(在适当的地方括号等),和 如果需要,转换您感兴趣的令牌 转换。
编辑:
FWIW:我一直在尝试使用auto
加上一个lambda作为
一种定义嵌套函数的方法。我的思绪不是那样的
这是否是一个好主意:我总是找不到
结果可读。但在这种情况下:
auto pushToken = [&]() {
if ( !currentToken.empty() ) {
tokens.push_back( currentToken );
currentToken = "";
}
}
在循环之前,然后用{替换所有if
pushToken()
。 (或者您可以创建一个数据结构
tokens
,currentToken
和pushToken
成员函数。
这甚至可以在pre-C ++ 11中使用。)
编辑:
最后一句话,因为OP似乎想要这样做
专门用std::istream
:解决方案
添加MustMatch
操纵器:
class MustMatch
{
char m_toMatch;
public:
MustMatch( char toMatch ) : m_toMatch( toMatch ) {}
friend std::istream& operator>>( std::istream& source, MustMatch const& manip )
{
char next;
source >> next;
// or source.get( next ) if you don't want to skip whitespace.
if ( source && next != m_toMatch ) {
source.setstate( std::ios_base::failbit );
}
return source;
}
}
正如@Angew指出的那样,你还需要一个>>
个月;通常,月份将表示为一个类,所以
你在这个问题上重载>>
:
std::istream& operator>>( std::istream& source, Month& object )
{
// The sentry takes care of skipping whitespace, etc.
std::ostream::sentry guard( source );
if ( guard ) {
std::streambuf* sb = source.rd();
std::string monthName;
while ( std::isalpha( sb->sgetc() ) ) {
monthName += sb->sbumpc();
}
if ( !isLegalMonthName( monthName ) ) {
source.setstate( std::ios_base::failbit );
} else {
object = Month( monthName );
}
}
return source;
}
当然,你可以在这里介绍很多变种:月份
例如,name可以限制为最多3个字符
(通过制作循环条件monthName.size() < 3 &&
std::isalpha( sb->sgetc() )
)。但如果你正在处理
在你的代码中以任何方式写几个月,写一个Month
类及其
>>
和<<
运营商是您必须尽快完成的事情
后来无论如何。
然后像:
source >> year >> dayOfYear >> MustMatch( '(' ) >> day >> month
>> MustMatch( ')' ) >> hour;
if ( !(source >> ws) || source.get() != EOF ) {
// Format error...
}
就是所需要的。 (像这样的操纵者的使用是 另一种值得学习的技巧。)
答案 1 :(得分:3)
scanf()
的@Angew +1。它将在一行中完成您想要的任务:
int day;
int hour;
char month[4];
int result = sscanf(ExampleString.c_str(), "%*d %*d (%d %3s) %d", &day, month, &hour);
if (result != 3)
{
// parse error;
}
答案 2 :(得分:2)
正则表达式http://coliru.stacked-crooked.com/a/ac5a4c9269e94344
的工作示例(不包括字符串解析)
#include <iostream>
#include <regex>
#include <string>
using namespace std;
int main()
{
//int year, dayOfYear, day;
//std::string month, leftParenthesis, rightParenthesis;
std::string ExampleString = "2013 336 (02 DEC) 04";
regex pattern("\\s*(\\d+)\\s+(\\d+)\\s*\\((\\d+)\\s+([[:alpha:]]+)\\)\\s*(\\d+)\\s*");
// Matching single string
std::smatch sm;
if (std::regex_match(ExampleString, sm, pattern)) {
cout << "year: " << sm[1].str() << endl;
cout << "dayOfYear: " << sm[2].str() << endl;
cout << "day: " << sm[3].str() << endl;
cout << "month: " << sm[4].str() << endl;
cout << "hour: " << sm[5].str() << endl;
}
cout << endl;
cout << endl;
// If your data contains multiple lines to parse, use this version
// unfortunately it will skip all lines that does not match pattern.
ExampleString = "2013 336 (02 DEC) 04" "\n2014 336 (02 DEC) 04" "\n2015 336 (02 DEC) 04";
for (sregex_iterator it(ExampleString.begin(), ExampleString.end(), pattern), end_it;
it != end_it; ++it)
{
cout << "year: " << (*it)[1].str() << endl;
cout << "dayOfYear: " << (*it)[2].str() << endl;
cout << "day: " << (*it)[3].str() << endl;
cout << "month: " << (*it)[4].str() << endl;
cout << "hour: " << (*it)[5].str() << endl;
cout << endl;
}
}
下面是debuggex,它不接受[[:alpha:]]
所以用\ w代替它,虽然[a-zA-Z]会更好:
\s*(\d+)\s+(\d+)\s*\((\d+)\s+(\w+)\)\s*(\d+)\s*
答案 3 :(得分:1)
如果你真的不想使用正则表达式,并且你想要一个看起来与你已经拥有的相似的hack ...你可以用空格替换字符串中的括号。 (我并不是说这是一个好的解决方案,但它值得了解。)
int year, dayOfYear, day, hour;
std::string month;
std::string ExampleString = "2013 336 (02 DEC) 04";
std::replace_if(ExampleString.begin(), ExampleString.end(), [](char c) { return c == '(' || c == ')'; }, ' ');
std::istringstream yearDayMonthHourStringStream( ExampleString );
yearDayMonthHourStringStream >> year >> dayOfYear >> day >> month >> hour;
答案 4 :(得分:1)
FWIW,您可以通过将左括号和右括号读入char
变量而不是字符串来使流方法工作,并且当它看到右括号时让month
解析停止。但是有点难看:
int year, dayOfYear, day;
std::string month;
char leftParenthesis, rightParenthesis;
std::string ExampleString = "2013 336 (02 DEC) 04";
std::istringstream yearDayMonthHourStringStream( ExampleString );
if (yearDayMonthHourStringStream >> year >> dayOfYear >> leftParenthesis
>> day >> std::ws &&
getline(yearDayMonthHourStringStream, month, ')') &&
yearDayMonthHourStringStream >> rightParenthesis >> hour &&
leftParenthesis == '(' && rightParenthesis == ')')
...use your variables...
else
...report bad input...
(<iomanip>
's std::ws
刚刚使用,因此ws
的容差始终保持一致。