C语言11-词法分析器

僤鯓⒐⒋嵵緔 提交于 2020-01-27 04:06:05

关于单词统计

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

#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;
}

词法分析器

所谓的词法分析器,就是找出一个“单词”的“词性”。
在这里插入图片描述

  • 如果有一系列的字母和数字(全部都是单字符),找出其中的数字,以下是代码实现:
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。)
    如下图:(状态机提炼如下)
    在这里插入图片描述

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

  • 利用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);
}

/*设置输出的颜色*/
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!