介绍CppShell
写在前面
最近心血来潮又造了个轮子,其实启发我的是bajdcc/jMiniLang中的管道思想,java运行着太慢,因而用C艹实现一把。
如题图所示,使用非常非常简单。
- range生成有限/无穷数列,`range 0`生成自然数无穷数列,`range 1 10`生成1到10
- take N,表示从有限/无穷数列中摘取前N行
- last N,表示从有限数列中取倒数N行,当然了如果数列是无穷的,那么GG
- load FILENAME,加载文件
- save FILENAME,保存至文件
设计思路
首先当然是解析命令行输入啦,然后则是处理与输出。
一、处理命令
假如命令是诸如`load 1.txt | uppercase | save 2.txt`,以“|”作分隔,分隔后得到单一程序命令行,再以空格作分隔。
用式子来表示是:
- 用户输入command_string
- applications = command_string.split('|')
对applications中每一app,app_args = app.split(' '),然后app_name = app_args[0],删除app_args[0],得到后面的参数arguments下面的任务是,根据app_name和arguments来创建应用程序。
二、创建应用程序
我们以`range 1 100 | save 2.txt`为例,意义为“生成1到100的数列,然后保存至文件”。
所以必须生成两个程序range和save,那么两者是什么关系呢?
思考一下:生成数列,将数列保存至文件。即:以数列作输入,文件为输出。得到:range => save。即save的输入是range的输出,是从前向后逐渐依赖的关系,换句话说,后者调用前者。
这里用到了装饰者模式,既然后者调用前者,那么后者将前者包裹起来即可。应用程序的创建涉及工厂模式,没什么大过花哨的C++技巧。
三、应用程序接口设计
其实也就是“流”接口的设计。参考众多流设计,这里我只实现最简单的:
- bool available() const,返回流是否可用/到末尾
- char next(),读取当前字符,并准备下个字符
应用程序只要重载这两个接口即可。
代码实现
1. Shell
void CShell::exec(const std::string& cmd)
{
auto s = std::split(cmd, '|');
std::vector<app_t> cmder;
std::vector<std::string> names;
std::vector<std::vector<std::string>> arg;
for (auto& str : s)
{
str = std::trim(str);
auto part = std::split(str, ' ');
if (part.empty())
return error("empty argument");
names.push_back(part[0]);
auto apt = CApp::get_type_by_name(part[0]);
if (apt == app_none)
return error("invalid application: " + str);
part.erase(part.begin());
cmder.push_back(apt);
arg.push_back(part); // 应用程序参数
}
auto inner = CApp::create(app_null); // 最里层程序
std::shared_ptr<CApp> app;
for (uint32_t i = 0; i < cmder.size(); i++)
{
app = CApp::create(cmder[i]); // 工厂模式创建应用程序
if (app->set_arg(arg[i]) != 0)
return error(names[i] + ": " + app->get_err());
app->set_inner_app(inner); // 装饰模式进行包装
inner = app;
}
while (app->available()) // 正式工作!
{
auto c = app->next();
if (c != '\0')
std::cout << c;
}
} void CShell::error(const std::string& str)
{
std::cerr << str << std::endl;
}
2. App
enum app_t
{
app__begin,
app_none,
app_null,
app_pipe,
app_range,
app_take,
app_last,
app_load,
app_save,
app__end
}; class CApp
{
public:
CApp();
virtual ~CApp(); static std::shared_ptr<CApp> create(app_t type);
static app_t get_type_by_name(const std::string &name); int set_arg(std::vector<std::string> arg);
virtual int init() = 0; void set_inner_app(std::shared_ptr<CApp> app); std::string get_err() const; virtual bool available() const = 0;
virtual char next() = 0; protected:
std::vector<std::string> args;
std::string error;
std::shared_ptr<CApp> inner;
}; // 创建
std::shared_ptr<CApp> CApp::create(app_t type)
{
switch (type)
{
case app_none:
break;
case app_null:
return std::make_shared<CAppNull>();
case app_pipe:
return std::make_shared<CAppPipe>();
case app_range:
return std::make_shared<CAppRange>();
case app_take:
return std::make_shared<CAppTake>();
case app_last:
return std::make_shared<CAppLast>();
case app_load:
return std::make_shared<CAppLoad>();
case app_save:
return std::make_shared<CAppSave>();
default:
break;
}
assert(!"invalid type");
return nullptr;
}
3. AppLoad
就举这一个例子吧
int CAppTake::init() // 初始化
{
if (args.size() == 1) // 有一个参数
{
start = 1; // 计数开始
end = atoi(args[0].c_str()); // 计数结束
}
else
{
error = "invalid argument size";
return -1;
}
return 0;
} bool CAppTake::available() const
{
return start <= end || !data.empty();
} char CAppTake::next()
{
if (data.empty())
{
if (!available()) // 上一流已经中止
return '\0';
while (inner->available()) // 上一流有数据
{
auto c = inner->next();
data.push(c);
if (c == '\n') // 读取一行到data中
break;
}
start++; // 计数加一
if (data.empty()) // 没有数据了
return '\0';
}
auto ch = data.front(); // 输出读取的一行数据
data.pop();
return ch;
}
阶段性总结
总之,做这个轮子还是挺愉悦的~因为并未脱离舒适区。。就当复习吧。
好吧,其实写这玩意是因为bash中的awk、sed、grep等查找替换太复杂了,还不如自己做个。
由https://zhuanlan.zhihu.com/p/26591115备份。
介绍CppShell的更多相关文章
- CSS3 background-image背景图片相关介绍
这里将会介绍如何通过background-image设置背景图片,以及背景图片的平铺.拉伸.偏移.设置大小等操作. 1. 背景图片样式分类 CSS中设置元素背景图片及其背景图片样式的属性主要以下几个: ...
- MySQL高级知识- MySQL的架构介绍
[TOC] 1.MySQL 简介 概述 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司. MySQL是一种关联数据库管理系统,将数据保存在不同的表中,而 ...
- Windows Server 2012 NIC Teaming介绍及注意事项
Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...
- Linux下服务器端开发流程及相关工具介绍(C++)
去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...
- JavaScript var关键字、变量的状态、异常处理、命名规范等介绍
本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...
- HTML DOM 介绍
本篇主要介绍DOM内容.DOM 节点.节点属性以及获取HTML元素的方法. 目录 1. 介绍 DOM:介绍DOM,以及对DOM分类和功能的说明. 2. DOM 节点:介绍DOM节点分类和节点层次. 3 ...
- HTML 事件(一) 事件的介绍
本篇主要介绍HTML中的事件知识:事件相关术语.DOM事件规范.事件对象. 其他事件文章 1. HTML 事件(一) 事件的介绍 2. HTML 事件(二) 事件的注册与注销 3. HTML 事件(三 ...
- HTML5 介绍
本篇主要介绍HTML5规范的内容和页面上的架构变动. 目录 1. HTML5介绍 1.1 介绍 1.2 内容 1.3 浏览器支持情况 2. 创建HTML5页面 2.1 <!DOCTYPE> ...
- ExtJS 4.2 介绍
本篇介绍ExtJS相关知识,是以ExtJS4.2.1版本为基础进行说明,包括:ExtJS的特点.MVC模式.4.2.1GPL版本资源的下载和说明以及4种主题的演示. 目录 1. 介绍 1.1 说明 1 ...
随机推荐
- Spark Streaming性能调优详解(转)
原文链接:Spark Streaming性能调优详解 Spark Streaming提供了高效便捷的流式处理模式,但是在有些场景下,使用默认的配置达不到最优,甚至无法实时处理来自外部的数据,这时候我们 ...
- nmap常用扫描命令
NMap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包. nmap是在网络安全渗透测试中经常会用到的强大的扫描器.功能之强大,不言而喻.下面介绍一下它的几种扫描命令.具体的 ...
- CSS 中的强制换行和禁止换行
强制换行 1.word-break: break-all; 只对英文起作用,以字母作为换行依据. 2.word-wrap: break-word; 只对英文起作 ...
- 几款很厉害的jQuery数字化签名插件(转)
在浏览器中,我们有很多方式来绘制生成签名效果,并且有很多很棒很智能的jQuery插件.数字化签名是未来的发展方向,正是这个原因我们这里收集并且推荐了四款超棒的jQuery数字化签名插件,希望大家喜欢! ...
- Echarts使用dataset数据集管理数据
1.可以看官网api的入门例子 使用常见的对象数组的格式 option = { legend: {}, tooltip: {}, dataset: { // 这里指定了维度名的顺序,从而可以利用默认的 ...
- Windows 批处理 ping 某个网段
原文: https://blog.csdn.net/leuxcn/article/details/51288248 ------------------------------------------ ...
- [RSpec] LEVEL 2 CONFIGURATION & MATCHERS
Installing RSpec In this level we'll start by getting you setup on a regular Ruby project, then move ...
- (剑指Offer)面试题6:重建二叉树
题目: 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树. 假设输入的前序遍历和中序遍历结果中都不含重复的数字. 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...
- Java中equals和==的解析
java中的数据类型,可分为两类: 1.基本数据类型,也称原始数据类型.byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(== ...
- 通过反射获取class文件中的构造方法,运行构造方法
/* * 通过反射获取class文件中的构造方法,运行构造方法 * 运行构造方法,创建对象 * 1.获取class文件对象 * 2.从class文件对象中,获取需要的成员 * * Constructo ...