编译器构造中自底向上的LALR(1)语法分析的语法分析表生成的实现

2023-11-04

提示:阅读本文需掌握编译原理的相关基础知识

本文中使用C++语言系统地实现了龙书中LALR(1)语法分析表的构造算法,首先计算增广文法的LR(0)项集族,每一个项集只包含内核项,计算过程中自动生成了LR(0)自动机,该自动机使用基于十字链表存储结构的有向图表示。然后通过确定自发生成和传播的向前看符号算法计算各LR(0)内核项集自发生成的向前看符号(增广文法新增产生式的向前看符号不包括在内)并确定LR(0)内核项集之间向前看符号的传播关系。最后一遍扫描前将为增广文法新增产生式添加向前看符号即输入结束符$,然后传播向前看符号直到不能传播为止,扫描结束后就生成了LALR(1)内核项集族。随后调用函数计算各LALR(1)项集的非内核项集确定LALR(1)项集族,并得到LALR(1)自动机。最后根据LALR(1)自动机填写LALR(1)语法分析表完成语法分析表的生成工作。

程序测试用例为博主自行编写的正则表达式文法(文法部分地方写得较别扭,而且并非反映正则表达式全部特性的文法,对正反向预查的支持存在一些问题,请读者见谅),由于文法保存在文本中,需要由计算机读入分析,为了便于计算机理解需要将书写格式标准化,博主自行设计了书写格式,具体如下:

#1b 非终结符 非终结符 ---- #1e (非终结符号集合)

#2b 终结符 终结符 —- #2e   (终结符号集合)

#3b 增广文法开始符号 原文法开始符号 #3e

#4b

#b 非终结符(产生式头) $1($2) 非终结符或终结符 $1($2) 非终结符或终结符 —- #e  (产生式体)   ($1标志非终结符,$2标志终结符,第一个产生式必须为增广产生式,第二个产生式必须为原文法以原文法开始符号为头的产生式)


#4e

其中省略号表示相同格式子项的重复出现,按照以上格式书写的文法(该文法修改了无数遍,说多了都是泪)为:

#1b S’ S preSurvey E T M F G outSquare B V C B’ inSquare inSquareRange #1e

#2b \ SPECTRANMETA POSITIVE-SURE-PRE POSITIVE-NEGA-PRE NEGATIVE-SURE-PRE NEGATIVE-NEGA-PRE ) | CLOSURE ? GIVEN LBOUND ULBOUND CLOSURE-NONGREEDY LBOUND-NONGREEDY ULBOUND-NONGREEDY CAP NONPRECAP SPECTRAN TRANMETA UPPERALPHA LOWERALPHA DIGIT SPECMETA REVERSEREF ^ [ ] - OTHERMETA $ #2e

#3b S’ S #3e

#4b

#b S’ $1 S #e

#b S $1 E #e

#b S $1 preSurvey #e

#b preSurvey $1 E $2 POSITIVE-SURE-PRE $1 E $2 ) #e

#b preSurvey $1 E $2 POSITIVE-NEGA-PRE $1 E $2 ) #e

#b preSurvey $2 NEGATIVE-SURE-PRE $1 E $2 ) $1 E #e

#b preSurvey $2 NEGATIVE-NEGA-PRE $1 E $2 ) $1 E #e

#b E $1 E $2 | $1 T #e

#b E $1 T #e

#b T $1 T $1 M #e

#b T $1 M #e

#b M $1 M $2 CLOSURE #e

#b M $1 M $2 ? #e

#b M $1 M $2 GIVEN #e

#b M $1 M $2 LBOUND #e

#b M $1 M $2 ULBOUND #e

#b M $1 M $2 CLOSURE-NONGREEDY #e

#b M $1 M $2 LBOUND-NONGREEDY #e

#b M $1 M $2 ULBOUND-NONGREEDY #e

#b M $1 F #e

#b F $2 CAP $1 E $2 ) #e

#b F $1 G #e

#b F $2 NONPRECAP $1 E $2 ) #e

#b F $1 outSquare #e

#b outSquare $2 SPECTRAN #e

#b outSquare $2 TRANMETA #e

#b outSquare $2 \ #e

#b outSquare $2 SPECTRANMETA #e

#b outSquare $2 UPPERALPHA #e

#b outSquare $2 LOWERALPHA #e

#b outSquare $2 DIGIT #e

#b outSquare $2 SPECMETA #e

#b outSquare $2 REVERSEREF #e

#b outSquare $2 ^ #e

#b G $2 [ $1 B $1 V $1 C $2 ] #e

#b V $2 ^ #e

#b V #e

#b B $1 B $1 B’ #e

#b B $1 B’ #e

#b B’ $1 V $1 inSquareRange $2 - $1 inSquareRange #e

#b inSquareRange $2 SPECTRAN #e

#b inSquareRange $2 SPECMETA #e

#b inSquareRange $2 OTHERMETA #e

#b inSquareRange $2 UPPERALPHA #e

#b inSquareRange $2 LOWERALPHA #e

#b inSquareRange $2 DIGIT #e

#b inSquareRange $2 CLOSURE #e

#b inSquareRange $2 \ #e

#b inSquareRange $2 SPECTRANMETA #e

#b inSquareRange $2 ? #e

#b inSquareRange $2 CAP #e

#b inSquareRange $2 | #e

#b inSquareRange $2 ) #e

#b C $1 C $1 inSquare #e

#b C $1 inSquare #e

#b inSquare $1 inSquareRange #e

#b inSquare $2 NONPRECAP #e

#b inSquare $2 POSITIVE-SURE-PRE #e

#b inSquare $2 POSITIVE-NEGA-PRE #e

#b inSquare $2 NEGATIVE-SURE-PRE #e

#b inSquare $2 NEGATIVE-NEGA-PRE #e

#b inSquare $2 ULBOUND #e

#b inSquare $2 LBOUND #e

#b inSquare $2 ULBOUND-NONGREEDY #e

#b inSquare $2 LBOUND-NONGREEDY #e

#b inSquare $2 CLOSURE-NONGREEDY #e

#b inSquare $2 GIVEN #e

#b G $2 [ $1 B $2 ] #e

#b G $2 [ $1 V $1 C $2 ] #e

#4e

#1b和#1e之间的部分为文法非终结符,#2b和#2e之间的部分为文法终结符,S’为增广文法开始符,S为原文法开始符

#4b和#4e之间的部分为产生式,紧跟于#b之后的是产生式头,随后为产生式体, $2表示紧跟其后的文法符号为终结符,$1表示紧跟其后的文法符号为非终结符,所有这些构成了对上下文无关文法的完整描述。这里非终结符、终结符以及产生式的含义博主会在将来发布的有关构建正则表达式引擎的文章中详解,现在只需着眼于文法本身,不必关心它们的具体含义。

需要另外说明的是,文法设计不当会导致一些异常现象的发生,例如如果把以上文法第三道产生式的产生式头的非终结符S改为F(这样就允许了预查表达式任意的自嵌套),程序会在计算follow集时陷入死循环,原因是修改过的文法第6,7道产生式直接导致程序在计算出非终结符E的follow集前必须先计算出E的follow集,导致循环计算,这样计算follow集时会陷入死循环。可以验证,如果去掉产生式6、7程序就可以正常运行并输出结果(但是存在语法分析动作冲突)。

下面贴出代码

代码清单(C++):

见github 地址 https://github.com/naturerun/LALRAnalysisGenerator

注意,这里有两个头文件Priority_Queue.h和assistfunction.h。Priority_Queue.h存放的是博主自己实现的优先级队列类,之所以没有使用标准库提供的容器适配器是因为适配器实现的优先级队列缺少一些操作队列成员的必要方法,如以下代码中的begin(),end()且容器适配器提供的接口的调用约定和返回值不符号程序编写要求,此外优先级队列是用堆实现的,但计算LALR闭包的算法要求优先队列必须用数组实现,其成员按优先级从小到大排序,故楼主自行实现了优先级队列。Priority_Queue.h代码如下:

#include <iostream>
#include <list>
#include <functional>
using namespace std;

template <typename T>
class Priority_Queue    //设备等待队列类(优先级队列),队列数据元素为t
{
public:
    typedef typename list<T>::iterator iterator;
    Priority_Queue() = default;
    Priority_Queue(const function<bool(const T&,const T&)> &com) :comparator(com) {}
    ~Priority_Queue() = default;
    pair<bool, typename Priority_Queue<T>::iterator> Insert(const T &x);  //插入操作,返回的pair的first指示插入是否成功,second为指向插入元素的迭代器
    bool RemoveTop(T &x);     //删除最高优先级元素并用x将其返回
    bool GetTop(T &x) const;  //获取最高优先级元素并用x将其返回 
    void MakeEmpty() { Queue.clear(); }    //清空队列
    bool isEmpty() const { return Queue.empty(); }   //判断队列是否为空
    bool isFull() const { return Queue.size() == Queue.max_size(); }   //判断队列是否已满
    typename Priority_Queue<T>::iterator erase(const typename Priority_Queue<T>::iterator &p) { return Queue.erase(p); }  //删除队列中p所指元素返回被删元素的下一元素
    typename Priority_Queue<T>::iterator insert(const typename Priority_Queue<T>::iterator &p, const T &c) { return Queue.insert(p, c); }  //将c插入至p所指位置,返回指向插入元素的迭代器
    typename list<T>::size_type  GetSize() const { return Queue.size(); }   //获取队列实际大小
    iterator begin() { return Queue.begin(); }   //获取指向队列最高优先级元素的迭代器
    iterator end() { return Queue.end(); }   //获取队列尾后迭代器

private:
    function<bool(const T&, const T&)> comparator;  //比较T类型的可调用对象,左操作数小于右操作数返回true
    typename list<T>::iterator adjust();   //新元素加入队列后调整元素位置,使队列中各元素保持优先级关系
    list<T> Queue;
};

template <typename T>
typename list<T>::iterator Priority_Queue<T>::adjust()
{
    T temp = Queue.back();
    auto p = Queue.end();
    --p;
    p = Queue.erase(p);

    if (Queue.begin() != p)
        --p;
    else
    {
        return Queue.insert(p, temp);
    }

    while (true)
    {
        if (comparator(temp, (*p)))
        {
            if (p != Queue.begin())
            {
                --p;
                if (p == Queue.begin())
                    continue;
            }
        }
        else
        {
            ++p;
            return Queue.insert(p, temp);
        }

        if (p == Queue.begin())
            break;
    }
    return Queue.insert(p, temp);
}

template <typename T>
pair<bool, typename Priority_Queue<T>::iterator> Priority_Queue<T>::Insert(const T &x)
{
    if (isFull())
        return { false, end() };
    else
    {
        Queue.push_back(x);
        return { true, adjust() };
    }
}

template <typename T>
bool Priority_Queue<T>::RemoveTop(T &x)
{
    if (isEmpty())
        return false;
    else
    {
        x = Queue.front();
        Queue.pop_front();
        return true;
    }
}

template <typename T>
bool Priority_Queue<T>::GetTop(T &x) const
{
    if (isEmpty())
        return false;
    else
    {
        x = Queue.front();
        return true;
    }
}

头文件assistfunction.h中定义了一些算法需要使用的辅助例程(只有第一个函数和算法有关,其他例程用于正则表达式引擎,后续会解读),由于代码简洁易懂,不是很重要,这里就不详细解说了。

#include<set>
#include<map>
#include<string>
void setToMap(const set<string> &source, map<string, int> &goal, int &count)
{
    for (set<string>::iterator p = source.cbegin(); p != source.cend(); ++p)
    {
        goal.insert(make_pair(*p, count++));
    }
}

char strToChar(const string &m)
{
    if (m.size() == 1)
        return m[0];
    else if (m.size() == 2)
    {
        switch (m[1])
        {
        case'f':    return '\f';
        case'n':    return '\n';
        case'r':    return '\r';
        case't':    return'\t';
        case'v':    return'\v';
        case'^':    return'^';
        case'-':    return'-';
        case'\\':    return'\\';
        case'*':    return'*';
        case'+':    return'+';
        case'?':    return'?';
        case'$':    return'$';
        case'.':    return'.';
        case'(':    return'(';
        case')':    return')';
        case':':    return':';
        case'=':    return'=';
        case'!':    return'!';
        case'<':    return'<';
        case'|':    return'|';
        case'[':    return'[';
        case']':    return']';
        case'{':    return'{';
        case'}':    return'}';
        }
    }
}

void insertIntoSet(map<size_t, set<size_t>> &beinserted, const size_t &source, const size_t &goal)
{
    map<size_t, set<size_t>>::iterator it = beinserted.find(goal);
    if (it == beinserted.end())
    {
        beinserted.insert(make_pair(goal, set<size_t>())).first->second.insert(source);
    }
    else
    {
        it->second.insert(source);
    }
}

void insertIntoMap(map<size_t, set<size_t>> &beinserted, map<size_t, map<size_t, set<size_t>>> &from)
{
    for (map<size_t, map<size_t, set<size_t>>>::iterator p = from.begin(); p != from.end(); ++p)
    {
        for (map<size_t, set<size_t>>::iterator q = p->second.begin(); q != p->second.end(); ++q)
        {
            map<size_t, set<size_t>>::iterator m = beinserted.find(q->first);
            if (m == beinserted.end())
            {
                beinserted.insert(make_pair(q->first, set<size_t>(q->second)));
            }
            else
            {
                m->second.insert(q->second.begin(), q->second.end());
            }
        }
    }
}

void insertIntoMap(map<size_t, map<size_t, map<size_t, size_t>>> &end, size_t substart, size_t subend, size_t sub_start_stackindex, size_t sub_end_stackindex)
{
    map<size_t, map<size_t, map<size_t, size_t>>>::iterator p = end.find(subend);
    if (p == end.end())
    {
        end.insert(make_pair(subend, map<size_t, map<size_t, size_t>>())).first->second.insert(make_pair(substart, map<size_t, size_t>())).first->second.insert(make_pair(sub_start_stackindex, sub_end_stackindex));
    }
    else
    {
        map<size_t, map<size_t, size_t>>::iterator q = p->second.find(substart);
        if (q == p->second.end())
        {
            p->second.insert(make_pair(substart, map<size_t, size_t>())).first->second.insert(make_pair(sub_start_stackindex, sub_end_stackindex));
        }
        else
        {
            q->second[sub_start_stackindex] = sub_end_stackindex;
        }
    }
}

程序的使用方法请参看main函数中的注释,分析结果会输出至指定文件,文件中加入了必要的逗号分隔符,将其扩展名改为csv用excel打开即可看到经整理的分析结果,如果文法不是LALR(1)的,程序运行时在命令行窗口上会打印语法分析动作冲突的相关信息。注意如果自定义文法进行测试文法需设计合理,避免存在逻辑错误,否则程序可能会陷入死循环或抛出异常。博主给出的示例文法可以得到分析结果且无语法分析冲突,可以把示例文法拷贝至输入文件中测试。

PS:本人微信号memoryundersun,欢迎各位同仁主动联系我,大家一起学习一起进步

2018.11.14重要更新


先前提到过改动文法会导致在计算follow集时陷入死循环,原因是循环依赖链造成的间接循环依赖,非终结符E的follow集依赖于另一个非终结符,另一个又依赖其他非终结符的follow集——这种依赖关系最终会延伸到E自身,造成E的follow集依赖于E的follow集的间接循环依赖。原先计算follow的算法不适用于间接循环依赖的情况,会陷入死循环,因而是错误的,所以简单调整一下follow集的计算算法,该算法中能够计算出follow集的非终结符最终会分离出去,剩下的是位于循环依赖链上的非终结符,此时对依赖链上的非终结符按照依赖关系反复迭代直到这些follow集的非终结符不再增大即可,最后把依赖链上非终结符follow集的计算结果加入已计算出follow集的非终结符表。分离出已计算出follow集的非终结符以避免对计算完成的非终结符的无效迭代,将非终结符依赖的其他非终结符从依赖关系中删去,并把被删去的非终结符的follow集的计算结果添加到被依赖非终结符的计算结果中以避免依赖非终结符对被依赖终结符的无效迭代。如果某一轮循环已完成follow集计算的非终结符构成的表不再增大,那么表中非终结符和尚未完成计算的非终结符依赖的非终结符集合交集始终为空集,故求交运算可以跳过,这些为必要的优化处理

修改过的follow集计算算法如下:(应该没有错误了,如果有请提出)

shared_ptr<map<string, set<string>>> LALRAutomata::calculateFollow()
{
    enum class GeneratingCycles {CURRENT_CYCLE, PREVIOUS_CYCLE};
    map<string, tuple<set<string>, set<string>, set<string>, bool>> temp;  //非终结符,当前follow,新增follow,依赖符号,是否计算完成
    map<string, pair<GeneratingCycles, map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator>> pre_and_cur_cycle_finish_follow_compute_info;  //非终结符 产生轮次 temp中对应项迭代器
    {
        auto h = temp.insert(make_pair(AugGraSS, tuple<set<string>, set<string>, set<string>, bool>())).first;
        get<0>(h->second).insert("$");
        get<1>(h->second).insert("$");
    }

    for (map<long, tuple<string, vector<ProductionBodySymbol>, set<string>>>::iterator p = productionSet.begin(); p != productionSet.end(); ++p)
    {
        for (vector<ProductionBodySymbol>::size_type i = 0; i < get<1>(p->second).size(); ++i)
        {
            if (get<1>(p->second)[i].TerminalOrNot == false)
            {
                if (i == get<1>(p->second).size() - 1)
                {
                    map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator it = temp.insert(make_pair(get<1>(p->second)[i].symbol, tuple<set<string>, set<string>, set<string>, bool>())).first;
                    get<2>(it->second).insert(get<0>(p->second));
                }
                else
                {
                    shared_ptr<set<string>> q = calculateFirst(get<1>(p->second), i + 1, get<1>(p->second).size() - 1);
                    map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator it = temp.insert(make_pair(get<1>(p->second)[i].symbol, tuple<set<string>, set<string>, set<string>, bool>())).first;
                    if (*(q->begin()) == "")
                    {
                        set<string>::iterator w = q->begin();
                        get<0>(it->second).insert(++w, q->end());
                        get<1>(it->second).insert(w, q->end());
                        get<2>(it->second).insert(get<0>(p->second));
                    }
                    else
                    {
                        get<0>(it->second).insert(q->begin(), q->end());
                        get<1>(it->second).insert(q->begin(), q->end());
                    }
                }
            }
        }
    }

    for (map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator p = temp.begin(); p != temp.end(); ++p)
    {
        if (get<2>(p->second).empty() == true)
        {
            get<3>(p->second) = true;
            pre_and_cur_cycle_finish_follow_compute_info.insert(make_pair(p->first, make_pair(GeneratingCycles::PREVIOUS_CYCLE, p)));
        }
        else
        {
            set<string>::iterator tempit;
            if ((tempit = get<2>(p->second).find(p->first)) != get<2>(p->second).end())
            {
                get<2>(p->second).erase(tempit);
                if (get<2>(p->second).empty() == true)
                {
                    get<3>(p->second) = true;
                    pre_and_cur_cycle_finish_follow_compute_info.insert(make_pair(p->first, make_pair(GeneratingCycles::PREVIOUS_CYCLE, p)));
                }
                else
                {
                    get<3>(p->second) = false;
                }
            }
            else
            {
                get<3>(p->second) = false;
            }
        }
    }

    bool first_set_has_changed = false;
    bool result_has_changed = false;
    bool result_has_changed_previous_run = true;
    bool is_first_cycle = true;
    while (true)
    {
        map<string, pair<GeneratingCycles, map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator>>::iterator n = pre_and_cur_cycle_finish_follow_compute_info.begin();
        for (map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator p = temp.begin(); p != temp.end(); ++p)
        {
            if (get<3>(p->second) == true)
            {
                if (pre_and_cur_cycle_finish_follow_compute_info.find(p->first) == pre_and_cur_cycle_finish_follow_compute_info.end())
                    get<1>(p->second).clear();
                continue;
            }

            if (is_first_cycle == false)
            {
                get<1>(p->second).clear();
            }

            set<string>::size_type size = get<0>(p->second).size();
            if (result_has_changed_previous_run)
            {
                map<string, pair<GeneratingCycles, map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator>>::iterator itleft = pre_and_cur_cycle_finish_follow_compute_info.begin();
                set<string>::iterator itright = get<2>(p->second).begin();
                while (itleft != pre_and_cur_cycle_finish_follow_compute_info.end() && itright != get<2>(p->second).end())
                {
                    if (itleft->first == *itright)
                    {
                        computeDifferenceSet(get<1>(itleft->second.second->second), get<0>(p->second), get<1>(p->second), false);
                        itright = get<2>(p->second).erase(itright);
                        ++itleft;
                    }
                    else if (itleft->first < *itright)
                    {
                        ++itleft;
                    }
                    else
                    {
                        ++itright;
                    }
                }
                if (get<2>(p->second).empty())
                {
                    pre_and_cur_cycle_finish_follow_compute_info.insert(make_pair(p->first, make_pair(GeneratingCycles::CURRENT_CYCLE, p)));
                    get<3>(p->second) = true;
                    result_has_changed = true;
                    continue;
                }
            }

            for (set<string>::iterator m = get<2>(p->second).begin(); m != get<2>(p->second).end(); ++m)
            {
                computeDifferenceSet(get<1>(temp[*m]), get<0>(p->second), get<1>(p->second), false);
            }

            if (get<0>(p->second).size() != size)
            {
                first_set_has_changed = true;
            }

            if (n != pre_and_cur_cycle_finish_follow_compute_info.end())
            {
                if (p->first == n->first)
                {
                    if (n->second.first == GeneratingCycles::CURRENT_CYCLE)
                    {
                        ++n;
                    }
                    else
                    {
                        n = pre_and_cur_cycle_finish_follow_compute_info.erase(n);
                    }
                }
            }
        }

        if (!first_set_has_changed && !result_has_changed)
        {
            break;
        }
        else
        {
            result_has_changed_previous_run = result_has_changed;
            first_set_has_changed = false;
            result_has_changed = false;
        }

        if (is_first_cycle)
            is_first_cycle = false;

        for (map<string, pair<GeneratingCycles, map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator>>::iterator temp = pre_and_cur_cycle_finish_follow_compute_info.begin(); temp != pre_and_cur_cycle_finish_follow_compute_info.end(); ++temp)
        {
            temp->second.first = GeneratingCycles::CURRENT_CYCLE;
        }
    }

    shared_ptr<map<string, set<string>>> result = make_shared<map<string, set<string>>>();
    for (map<string, tuple<set<string>, set<string>, set<string>, bool>>::iterator p = temp.begin(); p != temp.end(); ++p)
    {
        result->insert(make_pair(p->first, set<string>())).first->second.insert(get<0>(p->second).begin(), get<0>(p->second).end());
    }
    return result;
}

78-170行为迭代求解的核心步骤

主程序代码也进行了更新

2019.1.15更新


对LALR(1)自动机构造算法进行了优化,避免明显不等LALR项之间的冗余比较,提升生成性能

优化了LALR闭包生成算法,避免对相同非内核项点号右边符号串first集不必要的计算

2019.4.25更新

新增了firstK和followK集的生成算法,详见github readme文件

这个项目中Trie树的实现存在问题,有内存泄漏问题和删除的逻辑错误,但不影响程序的正确性,后续应该会修正


2021.12.14更新

重新修改了Trie树实现,修正了内存泄漏错误,并精简了代码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译器构造中自底向上的LALR(1)语法分析的语法分析表生成的实现 的相关文章

  • 为什么这个 Web api 控制器不并发?

    我有一个 Web API 控制器 里面有以下方法 public string Tester Thread Sleep 2000 return OK 当我调用它 10 次 使用 Fiddler 时 我预计所有 10 次调用都会在大约 2 秒后
  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • 前向声明类型和“已声明为类类型的非类类型”

    我对以下代码有问题 template
  • 从复选框列表中选择循环生成的复选框中的一个复选框

    抱歉我的英语不好 在我的 ASP NET 网站上 我从 SQL 表导入软件列表 看起来像这样 但实际上要长得多 Microsoft Application Error Reporting br br Microsoft Applicatio
  • 有些有助于理解“产量”

    在我不断追求少吸的过程中 我试图理解 产量 的说法 但我不断遇到同样的错误 someMethod 的主体不能是迭代器块 因为 System Collections Generic List 不是迭代器接口类型 这是我被卡住的代码 forea
  • 如何将 .txt 文件中的数据转换为 xml? C#

    我在一个文本文件中有数千行数据 我想通过将其转换为更容易搜索的内容来轻松搜索 我希望 XML 或其他类型的大型数据结构 尽管我不确定它是否是最好的对于我的想法 每行的数据如下所示 第 31 册 托马斯 乔治 32 34 154 每本书都不是
  • 如何在 C# Designer.cs 代码中使用常量字符串?

    如何在 designer cs 文件中引用常量字符串 一个直接的答案是在我的 cs 文件中创建一个私有字符串变量 然后编辑 Designer cs 文件以使用此变量 而不是对字符串进行硬编码 但设计者不喜欢这样抛出错误 我明白为什么这行不通
  • Eigen 和 OpenMP:由于错误共享和线程开销而没有并行化

    系统规格 Intel Xeon E7 v3 处理器 4 插槽 16 核 插槽 2 线程 核心 Eigen 系列和 C 的使用 以下是代码片段的串行实现 Eigen VectorXd get Row const int j const int
  • 如何使用 ASP.NET Core 获取其他用户的声明

    我仍在学习 ASP NET Core 的身份 我正在进行基于声明的令牌授权 大多数示例都是关于 当前 登录用户的 就我而言 我的 RPC 服务正在接收身份数据库中某个用户的用户名和密码 我需要 验证是否存在具有此类凭据的用户 获取该用户的所
  • 获取没有显式特征的整数模板参数的有符号/无符号变体

    我希望定义一个模板类 其模板参数始终是整数类型 该类将包含两个成员 其中之一是类型T 另一个作为类型的无符号变体T 即如果T int then T Unsigned unsigned int 我的第一直觉是这样做 template
  • 在 C# 中检查 PowerShell 执行策略的最佳方法是什么?

    当你跑步时Get ExecutionPolicy在 PowerShell 中 它得到有效的执行政策 https learn microsoft com en us powershell module microsoft powershell
  • 已发布的 .Net Core 应用程序警告安装 .Net Core,但它已安装

    我制作了一个 WPF 和控制台应用程序 供某人在我无法访问的私人服务器上使用 我使用 Visual Studio 2019 的内置 发布向导 来创建依赖于框架的单文件应用程序 当该人打开 WPF 应用程序时 他们会看到标准警告 他们单击 是
  • 模板外部链接?谁能解释一下吗?

    模板名称具有链接 3 5 非成员函数模板可以有内部链接 任何其他模板名称应具有外部链接 从具有内部链接的模板生成的实体与在其他翻译单元中生成的所有实体不同 我知道使用关键字的外部链接 extern C EX extern C templat
  • 如何在 C# 中创建异步方法?

    我读过的每一篇博客文章都会告诉您如何在 C 中使用异步方法 但由于某些奇怪的原因 从未解释如何构建您自己的异步方法来使用 所以我现在有这段代码使用我的方法 private async void button1 Click object se
  • 模板类中的无效数据类型生成编译时错误?

    我正在使用 C 创建一个字符串类 我希望该类仅接受数据类型 char 和 wchar t 并且我希望编译器在编译时使用 error 捕获任何无效数据类型 我不喜欢使用assert 我怎样才能做到这一点 您可以使用静态断言 促进提供一个 ht
  • 使动态创建的链接标签在 Winforms 中可点击

    我正在制作一个程序 允许用户单击由动态链接标签创建的公司名称 在我想知道如何做到这一点之前 我从未在 C 中使用过链接标签 可为特定用户生成的业务数量各不相同 因此每个用户的链接标签数量并不相同 然后我想捕获业务 ID 以进行 Json 调
  • 将 Lambda 表达式树与 IEnumerable 结合使用

    我一直在尝试了解有关使用 Lamba 表达式树的更多信息 因此我创建了一个简单的示例 这是代码 如果作为 C 程序粘贴到 LINQPad 中 它可以工作 void Main IEnumerable
  • WPF DataGrid / ListView 绑定到数组 mvvm

    我们假设你有 N 个整数的数组 表示行数的整数值 在模型中 该整数绑定到视图中的 ComboBox Q1 如何将数组 或数组的各个项目 绑定到 DataGrid 或 ListView 控件 以便 当您更改 ComboBox 值时 只有那么多
  • 当用户更改 Windows 中的语言键盘布局时如何通知?

    I want to show a message to user when the user changes the language keyboard layout of Windows for example from EN to FR

随机推荐

  • m = (++i)+(++i)+(++i) 问题

    m i i i 问题 问题描述 m i i i i初始值为1 求m计算结果 解析 计算机在计算m a b c d e f 先计算 a b c d 并把结果存储 例如 存储在j中 然后再计算j e f j 所以计算机先计算了两个 i 前两项i
  • SpringBoot+Netty实现WebSocket服务器

    前言 传统的请求 应答模式 http 越来越不能满足现实需求 服务器过于被动 而采用轮训或者long poll的方式过于浪费资源 这便有了WebSocket WebSocket是HTML5出的东西 协议 也就是说HTTP协议没有变化 或者说
  • 闲鱼x-sign参数

    据说淘宝的x sign程序已经人手一份了 闲鱼的好像不太多 最近研究了下闲鱼以x sign为代码的请求参数 包括x sign x mini wua x umt等等参数 效果如下 可以看到基本的请求参数和请求包数据都已经在里面了 上面的是po
  • 【React】react 性能优化的方式有哪些

    文章目录 1 Reac memo 缓存组件 2 使用 useMemo 缓存大量的计算 3 避免使用 内联对象 4 避免使用 匿名函数 5 延迟加载不是立即需要的组件 6 调整CSS而不是强制组件加载和卸载 7 使用React Fragmen
  • 两台虚拟机互相ping通(互相通讯)

    要是两台虚拟机能够PING通下列要求缺一不可 1 你所设置的虚拟网络的网络号不能跟外面你正在使用的真实的网络号一样 2 防火墙必须关闭 ubuntu命令 ufw disable 3 你设置的那俩台虚拟机必须在同一网段内 同一网段类似192
  • Ubuntu终端以及浏览器连接不上Github的解决办法

    项目场景 在安装一些其他库时 按照官网教程的步骤 其中需要利用ssh或者https方式从github克隆一些资源 问题描述 从github克隆下载资源会等待很久并且最后提醒失败 原因分析 网络原因 解决方案 用到的网站 站长工具 站长之家
  • 如何解决不可信输入带来的安全问题

    高质量程序设计艺术 样章连载 3 5 不可信输入 原书名 Code Quality The Open Source Perspective
  • Vue3通透教程【十七】Vite构建TS版本Vue项目

    文章目录 写在前面 创建TS版本的Vue3项目 插件安装 写在最后 写在前面 专栏介绍 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章 应粉丝要求开始更新 Vue3 的相关技术文章 Vue 框架目前的地位大家应该都晓得 所谓三大框
  • 当pycharm里的进程无法终止的情况

    当一直处于这种状态时 解决办法 在Run右边的tab栏 右键出现close tab 点击 之后便可以终止进程
  • MyBatisPlus多表查询的问题

    1 问题描述 有一个Person表和一个Pay表 person表中的id与pay表中ID一致 可以定位到一个人的pay情况 目前是想根据部门id person表中的一个字段 找到本部门下的pay 2 代码实现 根据部门id查询出person
  • 【计算机网络】传输层——TCP

    文章目录 TCP TCP协议的特点 TCP报文段 TCP连接管理 TCP连接的建立 TCP连接的释放 TCP可靠传输 序号 确认 重传 超时 冗余ACK 冗余确认 TCP流量控制 TCP拥塞控制 慢开始和拥塞避免 慢开始算法 拥塞避免算法
  • 图像分类、目标检测、语义分割、实例分割等计算机视觉方向基本概念

    参考原文 图像分类 目标检测 语义分割 实例分割和全景分割的区别 AI视觉网奇的博客 CSDN博客 1 图像分类 Object Classification 识别图片中存在的不同物体的种类 下方左图 人类 羊类 狗类 常用算法 KNN SV
  • GCC 的使用及介绍

    一 GCC介绍 Linux系统下的GCC是GNU推出的功能强大 性能优越的多平台编译器 它可以在多种硬件平台上编译处可执行程序的超级编译器 其执行效率比一般的编译器的效率要高20 30 Gcc编译器能将C C 语言源程序 汇程式化序和目标程
  • FPGA时序约束理论之多周期路径(6)

    1 单周期路径 前面的时钟周期约束 都是按照单周期关系进行分析数据路径 即数据的发起沿和采样沿是最邻近的一对时钟沿 如下图所示 默认情况下 保持时间的检查是以建立时间的检查为前提 即总是在建立时间的前一个时钟周期确定保持时间检查 也就是说
  • 基于Matlab的多线激光中心坐标值提取

    本文是基于给定的两张多线激光图片 如下图所示 需将图片中的激光线的中心线坐标提取出来并绘制激光中心线图形 因为是Matlab课程训练研究大作业 所以全文代码为Matlab 希望可以为相似作业的非专业同学提供一些帮助 文章目录 1 问题分析
  • docker-compose常用命令

    docker compose up d nginx 构建建启动nignx容器 docker compose exec nginx bash 登录到nginx容器中 docker compose down 删除所有nginx容器 镜像 doc
  • python 多分类逻辑回归_机器学习实践:多分类逻辑回归(softmax回归)的sklearn实现和tensorflow实现...

    本文所有代码及数据可下载 Scikit Learn 篇 Light 版 scikit learn内置了逻辑回归 对于小规模的应用较为简单 一般使用如下代码即可 from sklearn linear model logistic impor
  • Spring框架(SpringBoot)中redis报错(Could not get a resource from the pool、java.net.SocketTimeoutException)

    Spring框架 SpringBoot 中redis报错 在使用SpringBoot框架的时候 Spring一直会报两个特别纠结特别的烦的错误 尝试了很多种方法 都是失败的 不能成功 经过我坚持不懈的努力寻找 终于把问题给解决了 一 第一个
  • 灭鼠先锋

    奇技淫巧 cout lt lt LLLV
  • 编译器构造中自底向上的LALR(1)语法分析的语法分析表生成的实现

    提示 阅读本文需掌握编译原理的相关基础知识 本文中使用C 语言系统地实现了龙书中LALR 1 语法分析表的构造算法 首先计算增广文法的LR 0 项集族 每一个项集只包含内核项 计算过程中自动生成了LR 0 自动机 该自动机使用基于十字链表存