0

我想使用正则表达式为二次方程实现解析器。我想将其保留为控制台应用程序。我完成了正则表达式并在Debuggex中对其进行了测试。目前我有 2 个问题 - 我无法从 (ax^2+bx+c) 获取 a、b、c,并且我想使用向上和向下箭头添加类似 bash 的历史记录。提前致谢。我的代码:

#include <QCoreApplication>
#include <QRegExp>
#include <QString>
#include <QTextStream>
#include <QStringList>
#include <QDebug>
#include <cstdio>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Q_UNUSED(a);

    QTextStream cin(stdin, QIODevice::ReadOnly | QIODevice::Text);
    QTextStream cout(stdout, QIODevice::WriteOnly | QIODevice::Text);

    const QString regexText = R"(^[-]?\d*x\^2\s*[+,-]\s*\d*x\s*[+,-]\s*\d*$)";

    while(true)
    {
        QRegExp regex(regexText);

        cout << "Enter an equation to solve or press EOF(Ctrl+D/Z) to exit." << endl;
        cout << "--> " << flush;

        QString equation;
        equation = cin.readLine();

        if( equation.isNull() )
        {
            cout << endl;
            cout << "Thanks for using quadric equation solver! Exitting..." << endl;
            return 0;
        }

        int pos = regex.indexIn(equation);
        QStringList captures = regex.capturedTexts();
        qDebug() << captures;
    }
}
4

1 回答 1

2

我认为您正在寻求学习如何正确使用捕获组,而 debuggex 并不擅长向您显示结果。我会更多地沿着这些思路拍摄正则表达式:

^(-?\d*)x\^2\s*([+-]\s*\d*)x\s*([+-]\s*\d+)?$

您可以在我首选的 RegEx 工具RegExr中看到它的实际效果。将鼠标悬停在突出显示的匹配上以查看组捕获的内容。

您可以看到括号本质上是对可以单独提取并解析其含义的子表达式进行区分。我选择包含操作 (+/-),以便您可以使用它来解析系数的正负性质。您会在示例数据中看到它不包括十进制系数,但您的原始表达式也没有,我认为这回答了最紧迫的问题。

小数点

捕获小数就像在捕获的每组数字后添加一个片段一样简单:

(?:\.\d+)?

它可以选择匹配(不捕获)一个文字句点,后跟一些其他数字。这会将您更大的正则表达式变成:

^(-?\d*(?:\.\d+)?)x\^2\s*([+-]\s*\d*(?:\.\d+)?)x\s*([+-]\s*\d+(?:\.\d+)?)?$

如您所见,它允许捕获十进制表达式。它们仍然必须井井有条(正则表达式的一个缺点,但只有当您尝试一次完成所有事情时),但是您增加了可以解决的问题数量。

重新排序

下一步是处理乱序表达式。您可以在单个正则表达式中执行此操作,但出于以下几个原因,我建议您不要这样做:

  1. 读起来很糟糕,因此保持
  2. 在单个 RegEx 中执行此操作很难排除无关信息。
  3. 分段执行自动解决多个术语的问题(如x^2+x+x+2
  4. 分段进行可以让您更轻松地捕获高阶多项式。

1:验证

第一个基本步骤是确定一个术语的外观。对我来说,术语是一个运算符,后跟可选的空格,然后是变量表达式或常量。或者:

[+-]\s*(?:\d+(?:\.\d+)?|\d*(?:\.\d+)?x(?:\^\d+(?:\.\d+)?)?)

这太棒了,所以我将包括Debuggex可视化。

正则表达式可视化

围绕表达式的工作方式进行思考,因为它是下一个表达式的基本单位:

^-?\s*(?:\d+(?:\.\d+)?|\d*(?:\.\d+)?x(?:\^\d+(?:\.\d+)?)?)(?:\s*[+-]\s*(?:\d+(?:\.\d+)?|\d*(?:\.\d+)?x(?:\^\d+(?:\.\d+)?)?))+$

当您在Debuggex中看到那个时,很明显它基本上只是前一个表达式重复了一次或多次。我添加了一些空格并给第一个空格而不是运算符,但它本质上是相同的。

正则表达式可视化

现在,这里缺少一些空间,可以添加负数或减去正数。(想想,3x+ -4x^2),但这是对正则表达式的一个小改动,所以我想我会继续前进。将该正则表达式与您的行匹配(当然,修剪),您可以知道您有一个有效的等式。

2.提取

提取基于单个正则表达式,经过修改以捕获特定术语。它确实需要使用前瞻的能力,我必须承认一些正则表达式引擎不支持。但是 Debuggex 支持它,我没有找到 QRegExp 的确认或否认,所以我将它包含在内。

((?:^-?|[+-])\s*d*(?:\.\d+)?)

这是您的基本正则表达式。单独使用,它将捕获一个数字,而不管它是系数还是常数。要捕获常量,请添加负前瞻以确保它后面没有变量:

((?:^-?|[+-])\s*d*(?:\.\d+)?)(?!\s*x)

要捕获特定指数,只需匹配它,后跟空格或其他符号。:

((?:^-?|[+-])\s*d*(?:\.\d+)?)\S*x\^2(?=[\s+-])

要在没有指数的情况下进行捕获,请使用负前瞻来确保它丢失:

((?:^-?|[+-])\s*d*(?:\.\d+)?)\s*x(?!\^)

虽然,就个人而言,我更喜欢用这个一次捕获所有变量项:

((?:^-?|[+-])\s*d*(?:\.\d+)?)\s*x(?:^(\d+(?:\.\d+)?))

它恰好有两个捕获组:一个用于系数,一个用于指数。

于 2013-09-11T19:24:18.467 回答