欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言11-词法分析器

程序员文章站 2022-05-12 08:48:08
...

关于单词统计

因为连续空格算做一个。常规的算法如下:
选择一个标志,记录当前的是否为空格的状态,在状态切换之间,进行单词统计
代码实现如下:

#include "stdafx.h"


//统计单词个数
unsigned CountNumber(char* szInput)
{
    unsigned nRet = 0;
    char* pszChValue = szInput;
    
    int nIsSpaceFlag = 0;//所选择的状态标志来判断是否可以确定是下一个单词
    while (*pszChValue != '\0')
    {
        char chValue = *pszChValue;
        if (chValue == ' ')
        {

            if (nIsSpaceFlag != 1)
            {
                nRet++;
            }
            else
            {
                //meidongzuo
            }
            nIsSpaceFlag = 1;
        }
        else if (chValue != ' ')
        {
            nIsSpaceFlag = 0;
        }

        pszChValue++;
    }

    if (nIsSpaceFlag == 0)
    {
        nRet++;
    }

    return nRet;
}

int main(int argc, char* argv[])
{
    unsigned nRet = CountNumber("hello      from world   cool");
    printf("%d\r\n", nRet);
    return 0;
}

词法分析器

所谓的词法分析器,就是找出一个“单词”的“词性”。
C语言11-词法分析器

  • 如果有一系列的字母和数字(全部都是单字符),找出其中的数字,以下是代码实现:
int main(int argc, char* argv[])
{
    char* szInput = "a3skl4dfj5asl7dj6sa9dfasd";
    char* pszChValue = szInput;

    int i = 0;
    while (*pszChValue != '\0')
    {        
        if (*pszChValue < 'z' && *pszChValue > 'a')
        {
            printf("%c\t", *pszChValue);
            i++;
            if (i % 3 == 0)
            {
                printf("\r\n");
                i = 0;
            }
        }
        pszChValue++;
    }
    return 0;
}
  • 把问题升级一下,如果是正常的一段文本,你如何找出其中的数字呢?如果会找数字了,如何找出变量名呢(含关键字)?
int nValue1 = 123;
int nValue2 = 456;

回顾一下C语言中变量名(标识符)的要求:

  • 变量名:以字母或下划线开头,后接任意多个字母、下划线、数字。(nValue1,_myvalue223, _myvalue_hello)。
  • 数字:以数字开头,后接任意个数字(为了简化,我们只讨论十进制,比如123,456,9988。)
    如下图:(状态机提炼如下)
    C语言11-词法分析器

状态机转化为代码的常见方法就两种:

  • 利用switch…case(if…else)
  • 利用二维数组做状态转移数组(这次未说明)

判断词性的同时,拿到单词字符串

如果在找到单词或者数字时,再去取字符串,就已经有点晚了。
因此,我们多准备一个指针,用于进入到ID或者number时,指向ID或者number的初始位置。
等能够确定词性时,通过以上的指针,和当前指针位置,就可以得到词性对应的字符串。

// mytest_2.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<string.h>
#include "setdisplay.h"


#define STATE_INIT 0
#define STATE_NUMBER 1
#define STATE_ID 2
#define STATE_DELIM 3
#define STATE_WS 4

int g_TokenState = STATE_INIT;
int IsDelimiter(char chInput)
{//分隔符
    if (chInput == ';'
        || chInput == '\t'
        || chInput == '\r'
        || chInput == '\n'
        || chInput == ')'
        || chInput == '(')
    {
        return 1;
    }
    else
        return 0;
}

int IsBrackes(char chInput)
{//是括号,此处是bug还要考虑逻辑的顺序,之后回头再做
    if (chInput == '('
        || chInput == ')')
    {
        return 1;
    }
    else
        return 0;
}

int IsIdBegin(char chInput)
{//是字母和下划线开头确定为变量名
    if ((chInput >= 'a' && chInput <= 'z')
        || (chInput >= 'A' && chInput <= 'Z')
        || chInput == '_')
    {
        return 1;
    }
    else
        return 0;
}

int IsNumber(char chInput)
{//确定为数字
    if (chInput >= '0' && chInput <= '9')
    {
        return 1;
    }
    else
        return 0;
}

int IsKeyword(char chInput)
{
    if (chInput == 'i')
    {
        chInput++;
        if (chInput == 'n')
        {
            chInput++;
            if (chInput == 't')
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        return 0;
    }
    else
    {
        return 0;
    }
}


int IsIdCat(char chInput)
{
    if ((chInput >= 'a' && chInput <= 'z')
        || (chInput >= 'A' && chInput <= 'Z')
        || chInput == '_'
        || IsNumber(chInput))
    {
        return 1;
    }
    else
        return 0;
}

char g_chCurWord[256] = { 0 };
void TokenParse(char* szInput)
{
    char* pszValue = szInput;
    char* pszWordBegin = NULL;
    g_TokenState = STATE_INIT;
    while (*pszValue != '\0')//逐字符判断
    {
        char chCurValue = *pszValue;
        if (chCurValue == '(')
        {
            int xxxx = 0;
        }
        // nValue123;
        /******************1.遇到数字情况********************************/
        if (chCurValue > '0' && chCurValue < '9')
        {
            if (g_TokenState == STATE_NUMBER)
            {//保持数字判断的状态
                g_TokenState = STATE_NUMBER;
            }
            else if (g_TokenState == STATE_ID)
            {//保持id判断的状态
                g_TokenState = STATE_ID;
            }
            else if (g_TokenState == STATE_INIT
                || g_TokenState == STATE_DELIM
                || g_TokenState == STATE_WS)
            {//进入到了数字判断的状态
                g_TokenState = STATE_NUMBER;
                //保存首地址
                pszWordBegin = pszValue;
            }
        }
        /********************2.遇到分隔符情况***************************************/
        else if (IsDelimiter(chCurValue)
            || chCurValue == ' ')
        {
            if (g_TokenState == STATE_NUMBER)
            {//从判断数字状态吃了分隔符,确定数字
                g_TokenState = STATE_DELIM;
                //将当前的词性对应的字符串,拷贝到全局变量
                memset(g_chCurWord, 0, sizeof(g_chCurWord));
                memcpy(g_chCurWord, pszWordBegin, pszValue - pszWordBegin);
                //单词设置为白底黄字
                SetColor(COLOR_YELLOW, COLOR_WHITE);
                printf(g_chCurWord);
                //后输出当前的分隔符
                SetColor(COLOR_BLACK, COLOR_WHITE);
                printf("%c", chCurValue);
            }
            else if (g_TokenState == STATE_ID)
            {//从判断单词状态,吃了分隔符
                g_TokenState = STATE_DELIM;
                //将当前的词性对应的字符串,拷贝到全局变量
                memset(g_chCurWord, 0, sizeof(g_chCurWord));
                memcpy(g_chCurWord, pszWordBegin, pszValue - pszWordBegin);
                //单词设置为白底红字
                SetColor(COLOR_RED, COLOR_WHITE);
                printf(g_chCurWord);
                SetColor(COLOR_BLACK, COLOR_WHITE);
                printf("%c", chCurValue);
            }
            else
            {
                //设置白底黑字
                SetColor(COLOR_BLACK, COLOR_WHITE);
                printf("%c", chCurValue);
            }
        }
        /**************3.遇到字母下划线******************/
        else if (IsIdBegin(chCurValue))
        {
            if (g_TokenState == STATE_DELIM
                || g_TokenState == STATE_WS
                || g_TokenState == STATE_INIT)
            {//进入到判断单词状态
                g_TokenState = STATE_ID;
                //保存首地址
                pszWordBegin = pszValue;
            }
            else if (g_TokenState == STATE_ID)
            {
                if (IsIdCat(chCurValue))
                {//如果符合ID的要求,继续保持ID判断状态
                    g_TokenState = STATE_ID;
                }
            }
        }
        else 
        {
            SetColor(COLOR_BLACK, COLOR_WHITE);
            printf("%c", chCurValue);
        }
        pszValue++;
    }
}

int main(int argc, char* argv[])
{
    char* szTestExample = \
        "void fun()\r\n"\
        "{\r\n"\
        " int nValue1 = 123;\r\n"\
        " int nValue2 = 456;\r\n"\
        "}\r\n";
    TokenParse(szTestExample);
    return 0;
}

setdisplay.h

#pragma once
#include <Windows.h>

/*********** 宽度的基本单位(整数) ***********/
// 设置为2意味着1列为2个英文宽度或1个汉字宽度      
#define WIDTH_UNIT 2  // 可根据需求修改

/*******************************
功能:设置前背景色

f 前景色
b 背景色

原理:
1字节数据
低4字节为前景,高4字节为背景
********************************/
#define SetConsoleColor( f , b ) ( ( f ) + ( ( b ) << 0x04 ) )

/*************************************
颜色属性
**************************************/
#define COLOR_BLACK             0x00    // 黑色
#define COLOR_BLUE              0x01    // 蓝色
#define COLOR_GREEN             0x02    // 绿色
#define COLOR_LIGHTBLUE         0x03    // 湖蓝色
#define COLOR_RED               0x04    // 红色
#define COLOR_PURPLE            0x05    // 紫色
#define COLOR_YELLOW            0x06    // 黄色
#define COLOR_WHITE             0x07    // 白色
#define COLOR_GRAY              0x08    // 灰色
#define COLOR_THIN_BLUE         0x09    // 淡蓝色
#define COLOR_THIN_GREEN        0x0A    // 淡绿色
#define COLOR_THIN_LIGHT_GREEN  0x0B    // 淡浅绿色
#define COLOR_THIN_RED          0x0C    // 淡红色
#define COLOR_THIN_PURPLE       0x0D    // 淡紫色
#define COLOR_THIN_YELLOW       0x0E    // 淡黄色
#define COLOR_LIGHT_WHITE       0x0F    // 亮白色

void MoveTo(int nRow, int nCol);
void SetColor(unsigned char uchForeColor,
    unsigned char uchBackColor);

setdisplay.cpp

#include "setdisplay.h"


void SetColor(unsigned char uchForeColor,
    unsigned char uchBackColor)
{
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
        SetConsoleColor(uchForeColor,
        uchBackColor));
}

/*将光标移动到特定的行和列*/
void MoveTo(int nRow, int nCol)
{
    CONSOLE_CURSOR_INFO cii;
    cii.dwSize = 1;
    cii.bVisible = FALSE;
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cii);
    COORD loc;
    loc.X = nCol * (WIDTH_UNIT);
    loc.Y = nRow;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), loc);
}

/*设置输出的颜色*/
相关标签: C语言入门开发