关于单词统计
因为连续空格算做一个。常规的算法如下:
选择一个标志,记录当前的是否为空格的状态,在状态切换之间,进行单词统计
代码实现如下:
#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);
}
/*设置输出的颜色*/
来源:CSDN
作者:未北、
链接:https://blog.csdn.net/weixin_43365952/article/details/103958343