读取文本信息,将注释以及换行符和多余空格去除,生成字符流信息
每个字符都读取,交由一个类似于自动机功能的扫描器函数处理,并生成token流
读入经过预处理后的文本信息,将读取的第一个字符送入类自动机中,识别出它是字母、数字或者其它一些符号后把状态标记修改为它对应类型的标记值,然后通过switch送入到相应的类自动机识别功能模块中,自动识别成单独的一个有意义的word后,查找对应的字符表,如果有则生成一个token值,如果没有则在表中新增该word,并生成一个token值
下面的图是一个自动机设计的原理图,程序则根据这个原理图进行相关功能的模拟实现
// NEU,SHENYANG
// @author: plasma007
import java.io.File;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.util.*;
import javax.lang.model.element.Element;
import javax.print.attribute.standard.PrinterInfo;
import org.graalvm.compiler.nodes.NodeView.Default;
public class analyzer {
String[] k = { "int", "main", "void", "if", "char", "return", "short", "long", "short", "signed", "ensigned",
"restrict", "struct", "union", "enum", "typedef", "sizeof", "auto", "static", "register", "extern", "const",
"volatitle", "break", "goto", "else", "switch", "case", "default", "continue", "do", "double", "float" };// 关键字表
String[] p = { "+", "-", "*", "/", "%",".", "++", "--", ">", "<", "=", "(", ")", "{", "}", ";", "[", "]", ":", "?", "'",
"\"", ",", "==", ">=", "<=", "!=", "&", "|", "~", "^", "<<", ">>", "!", "&&", "||", "+=", "-=", "*=", "/=",
"%=", "&=", "|=", "^=", ">>=", "<==", "*", "&" };// 运算符和界符表
List<String> i = new ArrayList<String>();// 标识符表
List<String> C = new ArrayList<String>();// 单字符表
List<String> S = new ArrayList<String>();// 多字符表
List<String> c = new ArrayList<String>();// 常数表
public static void main(String[] args) throws Exception {
String path_in = "filename"; // 读入信息文件路径
String path_out = "filename"; // 输出信息文件路径
analyzer c_txt = new analyzer(); // 创建一个c_txt的analyzer对象
String p_in = c_txt.pretreatment(path_in);// p_in为经过预处理后的文本信息(调用了预处理函数)
c_txt.scanner(p_in, path_out);// 扫描器
}
// 预处理函数, 主要处理文档中的注释
public String pretreatment(String p_in) {
String r = ""; // 用来保存去除注释后的源程序
try {
File C_txt = new File(p_in);
InputStreamReader reader = new InputStreamReader(new FileInputStream(C_txt)); // 建立一个字符流对象reader(将输入的字节流转换为字符流)
BufferedReader br = new BufferedReader(reader);// 创建一个缓存字符流对象read
String line = ""; // 用于保存读取每行文本信息的
while (line != null) {
// 按行读取文本信息,遇到单行注释时删除注释内容
if (line.indexOf("//") != -1) { // 如果找到"//"则舍弃包括"//"及其后全部内容
line = line.substring(0, line.indexOf("//") - 1);
}
r = r + " " + line;// 将每一行用空格隔开
line = br.readLine();// 以行读取内容,每次只读取一行
}
br.close();// 关闭此文件输入流并释放与此流有关的所有系统资源
// 删除多行注释
while (r.indexOf("/*") != -1) {// 如果文本信息中函数注释符
if (r.indexOf("*/") != -1) {
// 如果找到注释符的闭包则将注释符连同注释符之间的内容删去
r = r.substring(0, r.indexOf("/*")).concat(r.substring(r.indexOf("*/") + 2));
} else {// 如果没找到注释符的闭包,则将注释符及其后内容全部删去
r = r.substring(r.indexOf("/*"));
}
}
r = r.replaceAll(" {2,}", " ");//删去多余的空格(至少匹配 2次)
if (r.charAt(0) == ' ')
r = r.substring(1);//删去首部空格
if (r.charAt(r.length() - 1) == ' ')
r = r.substring(0, r.length() - 1);// 删去尾部空格
System.out.println(r);// 将预处理后的string输出
} catch (Exception e) {
e.printStackTrace();
}
return r;// 返回处理好后的字符串
}
// 扫描器实现
public void scanner(String path_in, String path_out) {
try {
String result = "";// 用来保存token流信息
int subscript = 0;// 用来计数
int t, status, pre_subscript;// 分别为: 记录单个字符的ASCII码,记忆状态,用于记录当前字符下标
String word;// 形成一个单词
String r = path_in; // 经过预处理后的文本字符流信息
while (subscript < r.length()) {// 一直将文本信息读完
t = (int) r.charAt(subscript);// 将字符转换成ASCII码的值
if (t == 32)
status = 0;// 当前字符为空格时,状态标记为0
else if ((t > 64 && t < 91) || (t > 96 && t < 123))// 65~90 A-Z,97~122 a~z
status = 1;// 如果当前字符为字符时,状态标记为1
else if (t >= 48 && t <= 57)
status = 2;// 如果当前字符为数字时,状态标记为2
else if (t == 34)
status = 3;// 如果当前字符为双引号时,状态标记为3
else if (t == 39)
status = 4;// 如果当前字符为单引号时,状态标记为4
else if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96)
|| (t >= 123 && t <= 126))
status = 5;// 其它字符,状态标记为5
else {
// 检测到非法字符时输出异常信息
System.out.println("status异常");
System.out.println(r.charAt(subscript));
System.out.println((int) r.charAt(subscript));
break;
}
switch (status) {// 对于每一种状态
case 0:// 空格
subscript++; // 继续识别下一个字符
break;
case 1:// 字母(单词)
// result = check_letter();
pre_subscript = subscript; // 记录读入字符时的下标
if (subscript != r.length()) {// 当前字符在文本信息内
subscript++;// 更新下标
t = (int) r.charAt(subscript);// 更新当前字符值
while (((t > 64 && t < 91) || (t > 96 && t < 123) || (t > 47 && t < 58))
&& (subscript < (r.length() - 1))) {// 65~90 A-Z,97~122 a~z,48~57 0-9
// 如果当前字符为字母或者数字时,当前字符下标+1,否则将一直读到最后一个单词
subscript++;// 更新下标
t = (int) r.charAt(subscript);// 更新当前字符值
}
}
// if (pre_subscript == subscript)
// subscript++;// 如果后面不接字母或者数字,将识别为一个标识符
word = r.substring(pre_subscript, subscript);// 将这个词从串信息删去
int if_key = 0;// 判断是不是关键字
int m;
for (m = 0; m < k.length; m++) {
if (word.equals(k[m])) {
// System.out.println(m+" "+ k[m]);
if_key = 1;// 如果能匹配到则该改状态
break;
}
}
if (if_key == 1) {// 如果是匹配成功则在token上添加一个二元组{单词,在表中的序号} 由于序号是由1开始,所以应该为下标+1
result = result.concat(" {k,").concat(String.valueOf(m + 1)).concat("}");
} else {// 匹配失败,k表中没有则判断标识符表
if (i.contains(word)) {// 如果i标识符表不为空,且表中有这个单词
result = result.concat(" {i,").concat(String.valueOf(i.indexOf(word) + 1)).concat("}");
} else {
i.add(word);// 如果标识符表中没有这个单词,则添加到标识符表
result = result.concat(" {i,").concat(String.valueOf(i.indexOf(word) + 1)).concat("}");
}
}
break;
case 2:// 数字
pre_subscript = subscript;
if (subscript != r.length()) {// 如果当前字符在文本信息内
while (t >= 48 && t <= 57 && subscript < r.length() - 1) { // 48~57 0-9
//subscript++;
//t = (int) r.charAt(subscript);
if ((int) r.charAt(subscript + 1) == 46) {//如果后面是小数点
System.out.println(t);
subscript++;
t = (int) r.charAt(subscript);
} else {// 如果数字后面是数字
while (t >= 48 && t <= 57) {
subscript++;
t = (int) r.charAt(subscript);
if( t== 46) subscript++;t = (int) r.charAt(subscript);
}
}
}
}
// 获取该改长度的字符,得到一个字
word = r.substring(pre_subscript, subscript);
// 查找c常数表,如果有则直接插入位置
if (c.contains(word)) {
result = result.concat(" {c,").concat(String.valueOf(c.indexOf(word) + 1)).concat("}");
} else {// c常数表中没有,则c表新添
c.add(word);
result = result.concat(" {c,").concat(String.valueOf(c.size())).concat("}");
}
break;
case 3:// 字符串
pre_subscript = subscript;
subscript++;
// 从双引号后一位开始识别,非双引号字符一律划为字符串内容
while ((int) r.charAt(subscript) != 34 && subscript < r.length() - 1)
subscript++;
// 字符串需要将双引号一起包括进来
word = r.substring(pre_subscript, subscript + 1);
// 如果匹配不到成对的双引号,则报错
if ((int) word.charAt(word.length() - 1) != 34)
System.out.println("双引号不成对错误");
// 查找S表,如果S表中含有这个单词
if (S.contains(word)) {
result = result.concat(" {S,").concat(String.valueOf(S.indexOf(word) + 1)).concat("}");
} else {// 如果S表中没有这个单词,则添加这个单词进表
S.add(word);
result = result.concat(" {S,").concat(String.valueOf(S.indexOf(word) + 1)).concat("}");
}
subscript++;
break;
case 4:// 字符
pre_subscript = subscript;
subscript++;
// 从单引号后面开始识别,如果是单字符
while ((int) r.charAt(subscript) != 39 && subscript < r.length() - 1)
subscript++;
// 保存单引号和单字符作为一个整体
word = r.substring(pre_subscript, subscript + 1);
// 单引号匹配不成功,则报错
if ((int) word.charAt(word.length() - 1) != 39)
System.out.println("单引号不成对错误");
// 如果C单字符表中含有这个单词
if (C.contains(word)) {
result = result.concat(" {C,").concat(String.valueOf(C.indexOf(word) + 1)).concat("}");
} else {// 如果C没有则新增
C.add(word);
result = result.concat(" {C,").concat(String.valueOf(C.indexOf(word) + 1)).concat("}");
}
subscript++;// 转向下一字符
break;
case 5:// 符号
// 如果是符号,则直接将符号取出
word = r.substring(subscript, subscript + 1);
if (subscript + 1 < r.length()) {
t = (int) r.charAt(subscript + 1);
if ((t >= 33 && t <= 47 && t != 34 && t != 39) || (t >= 58 && t <= 64) || (t >= 91 && t <= 96)
|| (t >= 123 && t <= 126)) {
char f_point1 = r.charAt(subscript);
char f_point2 = r.charAt(subscript + 1);
if ((f_point1 == '&' && f_point2 == '&') || (f_point1 == '+' && f_point2 == '+')
|| (f_point1 == '-' && f_point2 == '-') || (f_point1 == '|' && f_point2 == '|')
|| (f_point1 == '>' && f_point2 == '=') || (f_point1 == '<' && f_point2 == '=')
|| (f_point1 == '=' && f_point2 == '=') || (f_point1 == '!' && f_point2 == '=')
|| (f_point1 == '<' && f_point2 == '<') || (f_point1 == '>' && f_point2 == '>')
|| (f_point1 == '+' && f_point2 == '=') || (f_point1 == '-' && f_point2 == '=')
|| (f_point1 == '+' && f_point2 == '+') || (f_point1 == '*' && f_point2 == '=')
|| (f_point1 == '/' && f_point2 == '=') || (f_point1 == '%' && f_point2 == '=')
|| (f_point1 == '&' && f_point2 == '=') || (f_point1 == '|' && f_point2 == '=')
|| (f_point1 == '^' && f_point2 == '=')) {
word = r.substring(subscript, subscript + 2);
subscript++;
}
}
}
int mm;
int if_point = 0;
// 查找p表
for (mm = 0; mm < k.length; mm++) {
if (word.equals(p[mm])) {
// System.out.println(m+" "+ k[m]);
if_point = 1;// 如果能匹配到则该改状态
break;
}
}
if (if_point == 1) {// 如果是匹配成功
result = result.concat(" {p,").concat(String.valueOf(mm + 1)).concat("}");
} else {// 匹配失败,k表中没有则判断标识符表
System.out.println("符号非法错误:出现非法符号,不是已定义符号 " + word);
}
subscript++;
break;
default:
System.out.println("\n字符识别异常:无法识别的状态");
break;
}
}
// 将结果保存在文档中
save_result(result, i, C, S, c, p, k, path_out);
} catch (Exception e) {
e.printStackTrace();
}
}
// 将list<String>转换成String用于保存信息写入文档
public String get_list(List<String> t) {
String s = new String();
for (int i = 1; i < (t.size() + 1); i++) {
s = s + "\n" + i + " " + t.get(i - 1);// 添加序号,单独存放在一行,形成一个列表
}
return s;// 返回一个字符串
}
// 将信息保存在文档中
public void save_result(String result, List<String> i, List<String> C, List<String> S, List<String> c, String[] p,
String[] k, String path_out) {
try {
String show_i, show_C, show_S, show_c, show_k, show_p;
show_i = "i标识符表: " + get_list(i);
show_C = "C单字符表:" + get_list(C);
show_S = "S多字符表:" + get_list(S);
show_c = "c常数表:" + get_list(c);
show_k = "k关键字表:" + get_list(Arrays.asList(k));
show_p = "p运算符和界符表:" + get_list(Arrays.asList(p));
File writename = new File(path_out); // 如果没有则新建一个新的path_out的txt文件
writename.createNewFile(); // 创建新文件
BufferedWriter out = new BufferedWriter(new FileWriter(writename));
out.write(result.concat("\n\n").concat(show_i).concat("\n\n").concat(show_C).concat("\n\n").concat(show_S)
.concat("\n\n").concat(show_c).concat("\n\n").concat(show_p).concat("\n\n").concat(show_k)); // 写入
out.flush(); // 把缓存区内容压入文件
out.close(); // 最后记得关闭文件
} catch (Exception e) {
e.printStackTrace();
}
}
}