编译原理学习一,去除代码中的注释
前言 开始学习编译原理了耶~ 关于编译原理的所有练习,按照老规矩,还是用我最喜欢的C#语言来实现,运行在.NetCore平台上~ 关于这个系列的所有代码已经上传到github了,项目主页: https://github.com/Deali-Axy/CompilerConstructionLearning 本次题目 对C或C++等高级程序设计语言编写的源程序中的//注释和/…/注释进行删除,保留删除
目录13 个章节
前言
开始学习编译原理了耶~ 关于编译原理的所有练习,按照老规矩,还是用我最喜欢的C#语言来实现,运行在.NetCore平台上~ 关于这个系列的所有代码已经上传到github了,项目主页:
本次题目
对C或C++等高级程序设计语言编写的源程序中的//注释和/…/注释进行删除,保留删除后的源程序。要求以文件形式进行保存。
思路分析
程序主要功能就是消除已经编写好的源程序中的注释。在源程序中注释有两种形式,一种是单行注释,用“//”表示,另一种是多行注释,用“/…/”表示。针对这两种形式,程序中用了if..else..语句加以判断,并做出相应的处理。在这里还有可能出现另一种情况,上述两种注释符号可能出现在引号中,出现在引号中的注释符号并没有注释功能,因此在引号中出现的注释符号不应该被消除。所以,这次编写的程序将要分三种情况分析。
第一种情况,单行注释:
if (ch != temp)
{
// 这里就是单行注释
ofile.put(ch);
ch = ifile.get();
}
或者
if (ch != temp)
{
/* 这里就是单行注释 */
ofile.put(ch);
ch = ifile.get();
}
第二种情况,块注释:
if (ifile.fail() || ofile.fail())
{
cerr << "open file fail\n";
return EXIT_FAILURE;
/*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报*
告打开文件失败*/
}
第三种情况,行后注释:
ifile.close(); // 关闭文件
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
还有一个关键的注意点
可以看到这一行
cout << "/////*////ret/rtr////";
这个字符串用双引号包起来的代码中有很多斜杠,所以要避免将这些斜杠识别为注释。 这里我用的方法是在处理注释前先把包含注释符号的字符串替换掉,等注释删除之后,再添加回去。
实现代码
注释写得很详细啦,配合上面的思路分析,我就不再继续分析代码了~
var sReader = new StreamReader(filePath);
var newSource = "";
var inBlock = false;
var replaceFlag = false;
var tempLine = ""; // 用于保存被替换的特殊行代码
while (!sReader.EndOfStream)
{
var line = sReader.ReadLine();
if (line.Length == 0) continue; // 去除空行
var quotationPattern = "^(.*?)\".*//.*\"";
var quotationResult = Regex.Match(line, quotationPattern);
if (quotationResult.Success)
{
System.Console.WriteLine("替换特殊代码,双引号中包裹注释斜杠");
tempLine = quotationResult.Groups[0].Value;
replaceFlag = true;
line = Regex.Replace(line, quotationPattern, REPLACEMENT);
}
// 单行注释
if (line.Trim().StartsWith(@"//"))
continue;
if (line.Trim().StartsWith(@"/*") && line.EndsWith(@"*/"))
continue;
// 注释块
if (Regex.Match(line.Trim(), @"^/\*").Success)
inBlock = true;
if (Regex.Match(line.Trim(), @"\*/$").Success)
{
inBlock = false;
continue;
}
// 行后注释
// 使用非贪婪模式(.+?)匹配第一个//
var pattern = @"^(.*?)//(.*)";
// var pattern = @"[^(.*?)//(.*)]|[^(.*?)/\*(.*)\*/]";
var result = Regex.Match(line, pattern);
if (result.Success)
{
System.Console.WriteLine("发现行后注释:{0}", result.Groups[2]);
line = result.Groups[1].Value;
}
// 还原被替换的代码
if (replaceFlag)
{
System.Console.WriteLine("还原特殊代码");
line = line.Replace(REPLACEMENT, tempLine);
replaceFlag = false;
}
if (inBlock) continue;
newSource += line + Environment.NewLine;
}
var outputPath = "output/exp1.src";
System.Console.WriteLine("去除注释完成,创建新文件。");
using (var sWriter = new StreamWriter(outputPath))
{
sWriter.Write(newSource);
}
System.Console.WriteLine("操作完成!文件路径:{0}", outputPath);
结果测试
源文件
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
int main()
{
cout << '/';
ifstream ifile; //建立文件流对象
ofstream ofile;
ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); //打开F盘根目录下的fileIn.txt文件
ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj");
if (ifile.fail() || ofile.fail())
{ //测试打开操作是否成功
cerr << "open file fail\n";
return EXIT_FAILURE;
/*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报*
告打开文件失败*/
}
char ch;
ch = ifile.get(); //进行读写操作
while (!ifile.eof())
{
if (ch == 34)
{ //双引号中若出现“//”,双引号中的字符不消除
char temp = ch; //第一个双引号
ofile.put(ch);
ch = ifile.get();
while (!ifile.eof())
{
if (ch != temp)
{ //寻找下一个双引号
ofile.put(ch);
ch = ifile.get();
}
else
{
ofile.put(ch);
break;
}
}
ch = ifile.get();
continue; //双引号情况结束,重新新一轮判断
}
if (ch == 47)
{ //出现第一个斜杠
char temp2 = ch;
ch = ifile.get();
if (ch == 47)
{ //单行注释情况
ch = ifile.get();
while (!(ch == '\n'))
ch = ifile.get();
}
else if (ch == '*')
{ //多行注释情况
while (1)
{
ch = ifile.get();
while (!(ch == '*'))
ch = ifile.get();
ch = ifile.get();
if (ch == 47)
break;
}
ch = ifile.get();
}
else
{
ofile.put(temp2); //temp2保存第一个斜杠,当上述两种情况都没有时,将此斜杠输出
}
//ch = ifile.get();
}
//cout << ch << endl;
ofile.put(ch); //将字符写入文件流对象中
ch = ifile.get(); //从输入文件对象流中读取一个字符
}
ifile.close(); //关闭文件
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
}
处理后的结果
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
int main()
{
cout << '/';
ifstream ifile;
ofstream ofile;
ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp");
ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj");
if (ifile.fail() || ofile.fail())
{
cerr << "open file fail\n";
return EXIT_FAILURE;
}
char ch;
ch = ifile.get();
while (!ifile.eof())
{
if (ch == 34)
{
char temp = ch;
ofile.put(ch);
ch = ifile.get();
while (!ifile.eof())
{
if (ch != temp)
{
ofile.put(ch);
ch = ifile.get();
}
else
{
ofile.put(ch);
break;
}
}
ch = ifile.get();
continue;
}
if (ch == 47)
{
char temp2 = ch;
ch = ifile.get();
if (ch == 47)
{
ch = ifile.get();
while (!(ch == '\n'))
ch = ifile.get();
}
else if (ch == '*')
{
while (1)
{
ch = ifile.get();
while (!(ch == '*'))
ch = ifile.get();
ch = ifile.get();
if (ch == 47)
break;
}
ch = ifile.get();
}
else
{
ofile.put(temp2);
}
}
ofile.put(ch);
ch = ifile.get();
}
ifile.close();
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
}
完整代码
https://github.com/Deali-Axy/CompilerConstructionLearning/blob/master/code/exp/exp1/Exp1.cs
参考资料:
- 正则表达式元字符:https://www.runoob.com/regexp/regexp-metachar.html
- JavaScript去除注释:https://segmentfault.com/a/1190000015611632
欢迎交流
交流问题请在微信公众号后台留言,每一条信息我都会回复哈~
- 微信公众号:画星星高手
- 打代码直播间:https://live.bilibili.com/11883038
- 知乎:https://www.zhihu.com/people/dealiaxy
- 专栏:https://zhuanlan.zhihu.com/deali
_EOF_
编译原理学习一,去除代码中的注释
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
操作系统学习二:进程同步与互斥之生产者-消费者问题--NetCore实现
目的 1. 掌握进程(线程)的同步与互斥。 2. 掌握生产者消费者问题的实现方法。 3. 掌握VC的多线程编程方法。 内容 本实验要求设计并实现一个进程,该进程拥有3个生产者线程和1个消费者线程,它们使用10个不同的缓冲区。需要使用如下信号量: 一个mutex信号量,用以阻止生产者线程和消费者线程同时操作缓冲区队列; 一个full信号量,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;
2022-06-17 · 计科基础 · 约 1 分钟
操作系统学习三:进程调度与死锁-以及银行家算法避免死锁--NetCore实现
前言 这是操作系统学习的第三篇啦,关于进程调度有很多内容,操作系统在调度进程的时候最容易遇到的问题就是死锁了,银行家算法是一个典型的避免死锁算法。 死锁的概念 先来了解一下死锁的基本概念:一组竞争系统资源或相互通信的进程相互的“永久”阻塞。若无外力作用,这组进程将永远不能继续执行。 看下面两幅图片,左边是可能产生死锁的状态,四辆汽车(进程)要竞争同一个资源(通过路口),如果系统调度不当,就会陷入死
2021-12-18 · 计科基础 · 约 1 分钟
奇怪的操作系统相关习题~4-21
操作系统作业 操作系统在计算机系统分层中处于哪个层次?它与上下层是怎样衔接的? 计算机系统的分层: 第1级是微程序机器层,这是一个实在的硬件层,它由机器硬件直接执行微指令。 第2级是传统机器语言,它也是一个实际的机器层,它由微程序解释机器指令系统。 第3级是操作系统层,它由操作系统程序实现。操作系统程序由机器指令和广义指令组成,这些广义指令是为了扩展机器功能而设置的,它是由操作系统定义和解释的软件
2022-06-17 · 计科基础 · 约 1 分钟
软件工程课程思维导图
结构如下 软件工程 什么是软件工程 方法 系统的 规范的 可量化的 过程 软件开发 运行 维护 构成的主要要素 人 项目 过程 方法 工具 软件制品 影响软件工程进步的动力 硬件能力 软件技术 社会需求 科学技术水平 软件的8个质量要素 正确性 软件满足需求规约的程度 完成用户目标的程度 可用性 学习、使用成本 操作软件 输入数据 解释软件输出结果 可靠性 完成预期功能概率 成功运行概率 有效性
2022-06-17 · 计科基础 · 约 1 分钟
讨论
共 0 条加载评论中…