2

我正在研究一个 RPN 计算器,现在我的 main.cpp 以及这些其他类文件应该接受一串代数运算,例如“3 + 4 / 2.34”,它可以采用算术运算符并且可以读取双精度数,整数、分数和混合分数(一个整数、一个空格,然后是一个分数)。

在命令控制台中,我收到此错误:

ASSERT: "uint(i) < uint(size())" in file c:\QtSDK\Desktop\Qt\4.8.1\mingw\include
/QtCore/qstring.h, line 701

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
Press <RETURN> to close this window...

我对 QT 的理解不够好,无法找出这个错误的来源,所以除了显示我的代码并希望有人能帮助我之外,我没有其他选择。有很多代码:/我已经排除了 Fraction 和 Mixed 类的头文件和源代码,因为它们不使用编译器似乎在抱怨的 qstring.h。如果您认为这些是必需的,我可以发布它们。

据我所知,它与将无符号整数与整数进行比较有关,可能试图将负整数分配给 uint?这是奇怪的部分,在 main.cpp 文件中,输入字符串是“1345/43143 - 2”,这会导致错误。此外,“3+4”也会导致错误。奇怪的是,如果我将输入字符串设置为“2341 + (23 ^ 4.421) / 23/321 - 5 6/7”,它确实有效,但似乎没有其他输入字符串有效。

令牌.h

#ifndef TOKEN_H
#define TOKEN_H

#include "Mixed.h"
#include "Fraction.h"

class Token
{
public:
    Token();
    Token(const QString& Substring);
    void SetIntPart(const QString& Substring);
    void SetDoublePart(const QString& Substring);
    void SetFractionPart(const QString& Substring);
    void SetMixedPart(const QString& Substring);
    void SetOperatorPart(const QString& Substring);

    bool isDouble(const QString& Substring);
    bool isInt(const QString& Substring);
    bool isFraction(const QString& Substring);
    bool isMixed(const QString& Substring);
    bool isOperator(const QString& Substring);
    bool isNumber()
    { return (inttoken || doubletoken || fractiontoken || mixedtoken); }
    bool isLeftParen();
    bool isRightParen();
    bool isOperator();


    int IntPart() { return intpart; }
    double DoublePart() { return doublepart; }
    Fraction FractionPart() { return fractionpart; }
    Mixed MixedPart() { return mixedpart; }
    char OperatorPart() { return operatorpart; }
    bool IntToken() { return inttoken; }
    bool DoubleToken() { return doubletoken; }
    bool FractionToken() { return fractiontoken; }
    bool MixedToken() { return mixedtoken; }
    bool OperatorToken() { return operatortoken; }
    QString toQString();
    void Print(ostream& out) const;
    friend ostream& operator<<(ostream& out, const Token& T);

private:
    int intpart;
    double doublepart;
    Fraction fractionpart;
    Mixed mixedpart;
    char operatorpart;

    bool inttoken;
    bool doubletoken;
    bool fractiontoken;
    bool operatortoken;
    bool mixedtoken;

    void ClearBools();
    void ClearParts();
    void ClearAll() { ClearBools(); ClearParts(); }
};



#endif // TOKEN_H

解析器.h

#ifndef PARSER_H
#define PARSER_H

#include <iostream>
#include <cstdlib>
#include "Fraction.h"
#include "Mixed.h"
#include "Queue.h"
#include "Stack.h"
#include "Token.h"

class Parser
{
public:
    Parser();
    void LoadInputQueue(const QString& QS);
    void LoadOutputQueue();
    void CopyInputToString(QString &QS);
    void CopyOutputToString(QString &QS);
    bool isOperator(const QChar& Qch);
    bool isSpace(const QChar& Qch);
    bool isRightParen(const QChar& Qch);
    bool isLeftParen(const QChar& Qch);
    bool isParen(const QChar& Qch);
    bool isDigit(const QChar& Qch);
    void PrintInputQueue(ostream& out);
    void PrintOutputQueue(ostream& out);
    void PrintOperatorStack(ostream& out);
    int precedence(Token T);

private:
    Queue<Token> InputQueue;
    Queue<Token> OutputQueue;
    Stack<Token> OperatorStack;

    void Tokenize(const QString& InputString, int n);
};

#endif // PARSER_H

主文件

#include <QtCore/QCoreApplication>
#include <cstdlib>
#include <iostream>
#include "Token.h"
#include "Parser.h"

using namespace std;

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

    /*cout << "Fractiontoken: " << A.FractionToken() << endl;
    cout << "Fraction part: " << A.FractionPart() << endl;
    cout << "Inttoken: " << A.IntToken() << endl;
    cout << "Int part: " << A.IntPart() << endl;
    cout << "Doubletoken: " << A.DoubleToken() << endl;
    cout << "Double part: " << A.DoublePart() << endl;
    cout << "Mixedtoken: " << A.MixedToken() << endl;
    cout << "Mixed part: " << A.MixedPart() << endl;
    cout << "Operatortoken: " << A.OperatorToken() << endl;
    cout << "Operator part: " << A.OperatorPart() << endl;*/

    QString inputstring = "1345/43143 - 2";
    Parser P;
    QString displaystring;

    P.LoadInputQueue(inputstring);
    P.CopyInputToString(displaystring);
    cout << qPrintable(displaystring);
    cout << endl;

    P.LoadOutputQueue();
    P.CopyOutputToString(displaystring);
    cout << qPrintable(displaystring);
    cout << endl;

    return a.exec();
}

解析器.cpp

#include "Parser.h"
enum PARENTHESIS_1{P1};
enum PARENTHESIS_2{P2};

bool Parser::isOperator(const QChar& Qch)
{
    char ch = Qch.toAscii();
    if (ch == '+' ||
            ch == '-' ||
            ch == '*' ||
            ch == '/' ||
            ch == '(' ||
            ch == ')' ||
            ch == '^' )
        return 1;
    return 0;
}

bool Parser::isSpace(const QChar& Qch)
{
    if (Qch.toAscii() == ' ')
        return 1;
    return 0;
}

bool Parser::isLeftParen(const QChar& Qch)
{
    char ch = Qch.toAscii();
    if (ch == '(')
        return 1;
    return 0;
}

bool Parser::isRightParen(const QChar& Qch)
{
    char ch = Qch.toAscii();
    if (ch == ')')
        return 1;
    return 0;
}

bool Parser::isParen(const QChar& Qch)
{
    if (isRightParen(Qch) || isLeftParen(Qch))
        return 1;
    return 0;
}

bool Parser::isDigit(const QChar &Qch)
{
    char ch = Qch.toAscii();
    if (ch > 47 && ch < 58)
        return 1;
    return 0;
}


Parser::Parser() : InputQueue(100), OutputQueue(100)
{

}

void Parser::Tokenize(const QString& InputString, int n)
{
    if (n >= InputString.size())
        return;
    else
    {
        if (isOperator(InputString[n]))
        {
            QString temp;
            temp.append(InputString[n]);
            Token temptoken(temp);
            InputQueue.enqueue(temptoken);
            Tokenize(InputString, n+1);
        }
        else if (isSpace(InputString[n]))
            Tokenize(InputString, n+1);
        else
        {
            QString temp;
            while (n < InputString.size() && !isSpace(InputString[n]) && !isParen(InputString[n]))
            {
                temp.append(InputString[n]);
                n++;
            }
            if (isSpace(InputString[n]))
            {
                if (isDigit(InputString[n+1]))
                {
                    temp.append(' ');
                    n++;
                    while (n < InputString.size() && !isSpace(InputString[n]) &&
                                    !isParen(InputString[n]))
                    {
                        temp.append(InputString[n]);
                        n++;
                    }
                    Token temptoken(temp);
                    InputQueue.enqueue(temptoken);
                    Tokenize(InputString, n);
                }
                else if (isOperator(InputString[n+1]))
                {
                    Token temptoken(temp);
                    InputQueue.enqueue(temptoken);
                    Tokenize(InputString, n+1);
                }
            }
            else if (isParen(InputString[n]))
            {
                Token temptoken(temp);
                InputQueue.enqueue(temptoken);
                temp.clear();
                temp.append(InputString[n]);
                Token temptoken2(temp);
                InputQueue.enqueue(temptoken2);
                Tokenize(InputString, n+1);
            }

        }


     }
}

int Parser::precedence(Token T)
{
    if (T.OperatorToken())
    {
        char temp = T.OperatorPart();
        if (temp == '^')
            return 4;
        if (temp == '*' || temp == '/')
            return 3;
        if (temp == '+' || temp == '-')
            return 2;
    }
    return 1;
}

void Parser::LoadOutputQueue()
{
    while (!InputQueue.empty())
    {
        Token temptoken;
        InputQueue.dequeue(temptoken);
        if (temptoken.isNumber())
            OutputQueue.enqueue(temptoken);
        else if (temptoken.isLeftParen())
            OperatorStack.push(temptoken);
        else if (temptoken.isRightParen())
        {
            while (!OperatorStack.empty() && !(OperatorStack.Peek()).isLeftParen())
            {
                Token temp;
                OperatorStack.pop(temp);
                OutputQueue.enqueue(temp);
            }
            if (!OperatorStack.empty() && (OperatorStack.Peek()).isLeftParen())
                OperatorStack.pop(temptoken);
            else if (OperatorStack.empty())
                throw P1;
        }
        else if (temptoken.isOperator())
        {
            while (!OperatorStack.empty() && precedence(temptoken) < precedence(OperatorStack.Peek()))
            {
                Token temp;
                OperatorStack.pop(temp);
                OutputQueue.enqueue(temp);
            }
            OperatorStack.push(temptoken);
        }

    }
    while (!OperatorStack.empty())
    {
        Token temptoken;
        OperatorStack.pop(temptoken);
        if (temptoken.isLeftParen())
            throw P2;
        OutputQueue.enqueue(temptoken);
    }


}

//this function takes a QString (of chars) and from that builds its queue of tokens
void Parser::LoadInputQueue(const QString &QS)
{
    Tokenize(QS,0);
}

void Parser::CopyOutputToString(QString &QS)
{
    QString tempstring;
    Token temptoken;
    for (int i=OutputQueue.Size()-1; i>=0; i--)
    {
        temptoken = OutputQueue.Element(i);
        tempstring.append(temptoken.toQString());
    }
    QS = tempstring;

}

void Parser::CopyInputToString(QString& QS)
{
    QString tempstring;
    Token temptoken;
    for (int i=InputQueue.Size()-1; i>=0; i--)
    {
        temptoken = InputQueue.Element(i);
        tempstring.append(temptoken.toQString());
    }
    QS = tempstring;
}

void Parser::PrintInputQueue(ostream& out)
{
    out << InputQueue << endl;
}

void Parser::PrintOutputQueue(ostream& out)
{
    out << OutputQueue << endl;
}

void Parser::PrintOperatorStack(ostream& out)
{
    out << OperatorStack << endl;
}

令牌.cpp

#include "Token.h"
#include <QStringList>

enum TOKEN_EXCEPTIONS{UNKNOWN_TOKEN};

Token::Token()
{

}

Token::Token(const QString& Substring)
{
    if (isInt(Substring))
        SetIntPart(Substring);
    else if (isDouble(Substring))
        SetDoublePart(Substring);
    else if (isFraction(Substring))
        SetFractionPart(Substring);
    else if (isMixed(Substring))
        SetMixedPart(Substring);
    else if (isOperator(Substring))
        SetOperatorPart(Substring);
    else
    {
        cout << qPrintable(Substring) << endl;
        throw UNKNOWN_TOKEN;
    }
}

void Token::SetIntPart(const QString& Substring)
{
    ClearAll();
    intpart = Substring.toInt();
    inttoken = 1;
}

void Token::SetDoublePart(const QString& Substring)
{
    ClearAll();
    doublepart = Substring.toDouble();
    doubletoken = 1;

}

void Token::SetFractionPart(const QString& Substring)
{
    ClearAll();
    QStringList Q;
    Q = Substring.split('/');
    Fraction frac(Q[0].toInt(),Q[1].toInt());
    fractionpart = frac;
    fractiontoken = 1;

}

void Token::SetMixedPart(const QString& Substring)
{
    ClearAll();
    QStringList Q1, Q2;
    Q1 = Substring.split(' ');
    QString wholepart = Q1[0];
    Q2 = Q1[1].split('/');
    QString numeratorstring = Q2[0];
    QString denominatorstring = Q2[1];
    Mixed M(wholepart.toInt(),numeratorstring.toInt(),denominatorstring.toInt());
    mixedpart = M;
    mixedtoken = 1;
}

void Token::SetOperatorPart(const QString& Substring)
{
    ClearAll();
    operatorpart = Substring[0].toAscii();
    operatortoken = 1;
}

bool Token::isDouble(const QString& Substring)
{
    int pointcount=0;
    int intcount=0;
    int pointloc=0;

    for (int i=0; i<Substring.size(); i++)
    {
        if (isdigit(Substring[i].toAscii()))
            intcount++;
        if (Substring[i].toAscii() == '.')
        {
            pointcount++;
            pointloc = i;
        }
    }

    if (pointcount != 1)
        return 0;
    if (intcount != Substring.size() - 1)
        return 0;
    return 1;

}

bool Token::isInt(const QString& Substring)
{
    if (Substring[0].toAscii() == '0')
        return 0;

    for (int i=0; i<Substring.size(); i++)
    {
        if (!isdigit(Substring[i].toAscii()))
            return 0;
    }

    return 1;
}

bool Token::isFraction(const QString& Substring)
{
    int slashcount=0;
    int intcount=0;
    int slashloc=0;

    for (int i=0; i<Substring.size(); i++)
    {
        if (Substring[i].toAscii() == '/')
        {
            slashcount++;
            slashloc = i;
        }
        if (isdigit(Substring[i].toAscii()))
            intcount++;
    }

    if (slashcount != 1) // if there is not exactly 1 slash in substring
        return 0;
    if (intcount != Substring.size() - 1) //if the rest of the chars are not integers
        return 0;
    if (slashloc == 0 || slashloc == Substring.size() - 1) //if slash is at wrong location
        return 0;
    if (Substring[slashloc+1].toAscii() == '0' ||
            Substring[0].toAscii() == '0')
        return 0;
    return 1;
}

bool Token::isMixed(const QString& Substring)
{
    if (Substring[0].toAscii() == '0')
        return 0;

    int spacecount=0;
    int slashcount=0;
    int intcount=0;
    int slashloc=0;
    int spaceloc=0;

    for (int i=0; i<Substring.size(); i++)
    {
        if (isspace(Substring[i].toAscii()))
        {
            spacecount++;
            spaceloc = i;
        }
        if (isdigit(Substring[i].toAscii()))
            intcount++;
        if (Substring[i].toAscii() == '/')
        {
            slashcount++;
            slashloc = i;
        }
    }

//    cout << "spacecount: " << spacecount << endl;
//    cout << "slashcount: " << slashcount << endl;
//    cout << "intcount: " << intcount << endl;
//    cout << "slashloc: " << slashloc << endl;
//    cout << "spaceloc: " << spaceloc << endl;

    if (spacecount != 1)
        return 0;
    if (slashcount != 1)
        return 0;
    if (intcount != Substring.size() - 2)
        return 0;
    if (slashloc == 0 || slashloc == Substring.size()-1)
        return 0;
    if (spaceloc == 0 || spaceloc == Substring.size()-1)
        return 0;
    if (slashloc < spaceloc)
        return 0;
    if (!isdigit(Substring[slashloc-1].toAscii()) || !isdigit(Substring[slashloc+1].toAscii()))
        return 0;
    if (!isdigit(Substring[spaceloc-1].toAscii()) || !isdigit(Substring[slashloc+1].toAscii()))
        return 0;
    return 1;

}

bool Token::isOperator(const QString& Substring)
{
    if (Substring.size() != 1)
        return 0;
    if (Substring[0] == '+' ||
            Substring[0] == '-' ||
            Substring[0] == '*' ||
            Substring[0] == '^' ||
            Substring[0] == '/' ||
            Substring[0] == '(' ||
            Substring[0] == ')')
        return 1;
    return 0;

}

void Token::ClearBools()
{
    mixedtoken = 0;
    inttoken = 0;
    doubletoken = 0;
    fractiontoken = 0;
    operatortoken = 0;

}

void Token::ClearParts()
{
    intpart = 0;
    doublepart = 0;
    Fraction f(0);
    fractionpart = f;
    Mixed m(0);
    mixedpart = m;
    operatorpart = NULL;
}

QString Token::toQString()
{
    QString tempstring;
    if (inttoken)
    {
        QString temp;
        temp.setNum(intpart);
        tempstring.append('[');
        tempstring.append(temp);
        tempstring.append(']');
        return tempstring;
    }
    else if (doubletoken)
    {
        QString temp;
        temp.setNum(doublepart);
        tempstring.append('[');
        tempstring.append(temp);
        tempstring.append(']');
        return tempstring;
    }
    else if (fractiontoken)
    {
        Fraction F = fractionpart;
        QString temp;
        tempstring.append('[');
        temp.setNum(F.Numerator());
        tempstring.append(temp);
        tempstring.append('/');
        temp.setNum(F.Denominator());
        tempstring.append(temp);
        tempstring.append(']');
        return tempstring;
    }
    else if (mixedtoken)
    {
        Mixed M = mixedpart;
        QString temp;
        tempstring.append('[');
        temp.setNum(M.WholePart());
        tempstring.append(temp);
        tempstring.append(' ');
        temp.setNum(M.Numerator());
        tempstring.append(temp);
        tempstring.append('/');
        temp.setNum(M.Denominator());
        tempstring.append(temp);
        tempstring.append(']');
        return tempstring;
    }
    else if (operatortoken)
    {
        tempstring.append('[');
        tempstring.append(operatorpart);
        tempstring.append(']');
    }
    else
        throw UNKNOWN_TOKEN;
}

void Token::Print(ostream& out) const
{
    if (inttoken)
    {
        out << intpart;
    }
    else if (doubletoken)
    {
        out << doublepart;
    }
    else if (fractiontoken)
    {
        out << fractionpart;
    }
    else if (mixedtoken)
    {
        out << mixedpart;
    }
    else if (operatortoken)
    {
        out << operatorpart;
    }
    else
        throw 1;

}

ostream& operator<<(ostream& out, const Token& T)
{
    T.Print(out);
    return out;
}

bool Token::isLeftParen()
{
    if (operatortoken && operatorpart == '(')
        return 1;
    return 0;
}

bool Token::isRightParen()
{
    if (operatortoken && operatorpart == ')')
        return 1;
    return 0;
}

bool Token::isOperator()
{
    if (operatortoken)
        return 1;
    return 0;
}
4

4 回答 4

1

我认为这肯定是错误的:

       while (n < InputString.size() && !isSpace(InputString[n]) && !isParen(InputString[n]))
        {
            temp.append(InputString[n]);
            n++;
        }
        if (isSpace(InputString[n]))

应该:

       while (n < InputString.size() && !isSpace(InputString[n]) && !isParen(InputString[n]))
        {
            temp.append(InputString[n]);
            n++;
        }
        if (n < InputString.size() && isSpace(InputString[n]))

真正的错误可能在其他地方没有读取您的所有代码,而是在您运行到字符串结尾然后访问它之后的字符的地方类似。

在调试器中运行您的代码,并在它断言时查看它在您的代码中的位置。

于 2013-02-20T23:26:52.673 回答
1

长话短说——

ASSERT: "uint(i) < uint(size())" in file c:\QtSDK\Desktop\Qt\4.8.1\mingw\include
/QtCore/qstring.h, line 701

这基本上意味着您正在运行字符串长度。它也可能是引用未初始化或释放的内存区域的效果。

我最有根据的猜测是解析器正在尝试读取输入字符串长度,这表明某些停止条件没有得到满足。

知道这一点,您可能更容易找到解析器问题本身。

如果您有一个允许调试的环境(例如 eclipse),那就去吧。如果没有,您可以尝试在代码的不同位置插入各种调试 printf 语句(我将从 Parser 开始)并查看它在哪里遇到错误。

我查看了代码,但没有发现明显的问题。祝你好运!

于 2013-02-20T23:30:17.107 回答
0

有很多地方可以在不先检查索引的情况下访问字符(或类似的数组条目):

void Parser::Tokenize(const QString& InputString, int n)
            if (isSpace(InputString[n]))
            {
                if (isDigit(InputString[n+1]))
                ...
                else if (isOperator(InputString[n+1]))




void Token::SetFractionPart(const QString& Substring)
    Fraction frac(Q[0].toInt(),Q[1].toInt());


void Token::SetMixedPart(const QString& Substring)
    QString wholepart = Q1[0];
    Q2 = Q1[1].split('/');
    QString numeratorstring = Q2[0];
    QString denominatorstring = Q2[1];


void Token::SetOperatorPart(const QString& Substring)
    operatorpart = Substring[0].toAscii();


bool Token::isInt(const QString& Substring)
    if (Substring[0].toAscii() == '0')

bool Token::isMixed(const QString& Substring)
    if (Substring[0].toAscii() == '0')

通过在调试器中运行并查看堆栈跟踪,您可以更轻松地找到特定问题

于 2013-02-20T23:29:34.890 回答
0

以下是对您的代码的一些观察:

  1. 您正在返回 0 或 1 而不是falsetrue在返回 a 的函数中bool
  2. 您可以搜索它,而不是与每个运算符字符进行比较:

    bool is_operator(char c)
    {
    static const char operators_text[] = "+-/*()^";
    std::string operators_str(operators_text);
    return operators_str.find_first_of(c) != std::string::npos;
    }

  3. 的 is-a函数可以简化:

    bool Parser::isSpace(const QChar& qch)
    {
    return qch == ' ';
    }

  4. 让您的 isDigit 更具可读性:

    bool Parser::isDigit(const Qchar& qch)
    {
    return (qch >= '0') || (qch <= '9');
    }

或使用std::isdigit().

于 2013-02-20T23:34:51.473 回答