首页 文章 精选 留言 我的

精选列表

搜索[数据库客户端],共10000篇文章
优秀的个人博客,低调大师

[雪峰磁针石博客]python库介绍-telnetlib:Telnet客户端

python测试开发项目实战-目录 python工具书籍下载-持续更新 python 3.7极速入门教程 - 目录 简介 telnetlib模块提供的Telnet类实现了Telnet协议(参见rfc854)。它为协议字符和telnet选项提供符号常量,符号常量来源于arpa/telnet.h,去掉了前缀TELOPT_。 telnet命令有: IAC, DONT, DO, WONT, WILL, SE (Subnegotiation End), NOP (No Operation), DM (Data Mark), BRK (Break), IP (Interrupt process), AO (Abort output), AYT (Are You There), EC (Erase Character), EL (Erase Line), GA (Go Ahead), SB (Subnegotiation Begin)。 class telnetlib.Telnet(host=None, port=0[, timeout]) 快速入门 #!/usr/bin/env python # -*- coding: utf-8 -*- # Author Rongzhong Xu 2016-08-25 wechat: pythontesting """ Name: telnet_demo.py Tesed in python3.5 """ import telnetlib HOST = "172.17.100.18" user = "test" password = "123456" def command(con, flag, str_=""): data = con.read_until(flag.encode()) print(data.decode(errors='ignore')) con.write(str_.encode() + b"\n") return data tn = telnetlib.Telnet(HOST) command(tn, "login: ", user) if password: command(tn, "Password: ", password) command(tn, "$", "ls") command(tn, "$", " exit") command(tn, "$", "") tn.close() 执行结果: $ python3 telnet_demo.py CentOS release 6.7 (Final) Kernel 2.6.32-573.18.1.el6.x86_64 on an x86_64 login: Password: Last login: Thu Aug 25 17:20:18 from 172.17.153.6 [test@public01 ~]$ ls [test@public01 ~]$ exit logout 参考资料 python测试等IT技术支持qq群: 144081101(后期会录制视频存在该群群文件) 591302926 567351477 本文最新版本地址 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 python官方文档:https://docs.python.org/3/library/telnetlib.html effbot: http://effbot.org/librarybook/telnetlib.htm rfc854: https://tools.ietf.org/html/rfc854.html 方法 Telnet.read_until(expected[, timeout]) 读取直到遇到了给定的字符串expected或超时秒数。 当找不到匹配时,返回读取内容。连接关闭或者没有数据时触发EOFError异常。 Telnet.read_all() 读取所有数据直到EOF,阻塞直到连接关闭。 Telnet.read_some() 读取至少一个字节的数据,除非EOF。 如果没有数据则阻塞。 Telnet.read_very_eager() 非阻塞地读取。连接关闭或者没有数据时触发EOFError异常。返回b''如果没有数据, Telnet.read_eager() 读取的已有数据。(快) 连接关闭或者没有数据时触发EOFError异常。返回b''如果没有数据, Telnet.read_lazy() 读取的已有数据(懒) 连接关闭或者没有数据时触发EOFError异常。返回b''如果没有数据, Telnet.read_very_lazy() 读取的已有数据(很懒) 连接关闭或者没有数据时触发EOFError异常。返回b''如果没有数据, Telnet.read_sb_data() 返回的SB/SE pair (suboption begin/end)之间的数据。此方法永远不会阻塞。 Telnet.open(host[, port[, timeout]]) 连接到主机。可选的第二个参数是默认为标准的 Telnet 端口 (23) 的端口号。可选的超时参数指定以秒计) 阻塞操作 (如连接尝试超时 (如果不指定,将使用全局默认超时设置)。 Telnet.msg(msg[, *args]) 当调试级别为> 0 打印调试消息。 Telnet.set_debuglevel(debuglevel) 设置调试级别。debuglevel越高信息越多。 Telnet.close() 关闭该连接。 Telnet.get_socket() 返回套接字供内部使用。 Telnet.fileno() 返回套接字对象内部使用的文件描述符。 Telnet.write(buffer) 写入字符串到套接字,加倍 IAC 的任何字符。连接关闭是可能触发OSError异常。 Telnet.interact() 交由用户控制。 Telnet.mt_interact() 多线程的版本的interact()。 Telnet.expect(list[, timeout]) 读取直到匹配正则表达式项列表中的一个。 list是一个正则表达式列表,包含编译(regex 对象)或未编译(字节字符串)。timeout以秒为单位, 默认值为无限期阻塞。 返回元组的三个项目: index为匹配正则表达式的位置;match 对象;此时读了的字节。 文件结尾或没有数据时触发EOFError异常,不匹配时返回(-1, None, data)。 如果正则表达式是贪婪模式或者如果多个表达式可以匹配相同的输入,结果是不确定的,可能取决于I/O定时。 Telnet.set_option_negotiation_callback(callback) 每次从输入流读取telnet选项时,调用callback(telnet socket, command (DO/DONT/WILL/WONT), option),后续步骤不会执行。

优秀的个人博客,低调大师

Spring 项目如何优雅的生成接口文档与客户端

背景 在开发 Restful 服务的过程中,大家或多或少都会碰到类似的问题,比如:接口如何文档化、怎样自动生成 Client。我们在开发 DMS 的过程,也碰到了类似的问题,并且积累了一些经验,借此跟大家分享,希望抛砖引玉。 概述 首先整体说下总的技术方案就是 Spring Boot + Springfox + Swagger Codegen ,其中 Spring Boot + Springfox 主要解决了接口如何文档化问题;Swagger Codegen 则主要解决了如何自动生成 Client 的问题。下面就详细介绍这套方案的实现细节。 具体方案 接口文档化 Spring Boot + Springfox Springfox 是为基于 Spring 构建的接口自动生成文档的工具,它的原理就是根据 Spring 接口层的注解生成符合 Swagger 规范的接口描述文件,然后通过内嵌的 Swagger UI 解析该描述文件并渲染出来。 对于这个这个方案,知道的人比较多,内网也有很多介绍该方案的文章,附录里我罗列了些,在此我就不具体阐述,下面我只介绍下我们使用的一些经验。 @Api 的 tags 属性请使用英文,该属性值在 Codegen 的时候会作为模块名。 创建多版本 Api 的方法 @Configuration @EnableSwagger2 @ComponentScan(basePackages = {"com.tmall.pegasus.dms.controller"}) public class SwaggerConfiguration { @Bean public Docket v20DocumentationPlugin() { return new VersionedDocket("2.0"); } @Bean public Docket v10DocumentationPlugin() { return new VersionedDocket("1.0"); } class VersionedDocket extends Docket { public VersionedDocket(String version) { super(DocumentationType.SWAGGER_2); super.groupName(version) .select() .apis(RequestHandlerSelectors.any()) .paths(regex(API_BASE_PATH + "/.*")) .build() .apiInfo(getApiInfo(version)) .pathProvider(new BasePathAwareRelativePathProvider(API_BASE_PATH)) // 这里记得设置 protocols,不然 Codegen 默认生成的 basePath 是 https 协议 .protocols(Sets.newHashSet("http")) .directModelSubstitute(LocalDate.class, String.class) .genericModelSubstitutes(ResponseEntity.class); } private ApiInfo getApiInfo(String version) { return new ApiInfo("Test Api", "Test Api Documentation", "1.0", "urn:tos", new Contact("xiaoming", "", "xiaoming@qq.com"), "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList()); } } @Bean UiConfiguration uiConfig() { return UiConfigurationBuilder.builder() .deepLinking(true) .displayOperationId(false) .defaultModelsExpandDepth(1) .defaultModelExpandDepth(1) .defaultModelRendering(ModelRendering.EXAMPLE) .displayRequestDuration(false) .docExpansion(DocExpansion.NONE) .filter(false) .maxDisplayedTags(null) .operationsSorter(OperationsSorter.ALPHA) .showExtensions(false) .tagsSorter(TagsSorter.ALPHA) .supportedSubmitMethods(UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS) .validatorUrl(null) .build(); } class BasePathAwareRelativePathProvider extends AbstractPathProvider { private String basePath; public BasePathAwareRelativePathProvider(String basePath) { this.basePath = basePath; } @Override protected String applicationPath() { return basePath; } @Override protected String getDocumentationPath() { return "/"; } @Override public String getOperationPath(String operationPath) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/"); return Paths.removeAdjacentForwardSlashes( uriComponentsBuilder.path(operationPath.replaceFirst(basePath, "")).build().toString()); } } } 在 Controller 上统一加上接口的前缀,尤其接口有多个版本的时候,该方式会非常方便。 @RequestMapping(path = "/api/v1") public class DataController { } @RequestMapping(path = "/api/v2") public class DataControllerV2 { } 方法注解记得设置 nickName,设置该属性的好处是在 Codegen 的时候会用该属性值作为对应的接口方法名。 @ApiOperation(value = "新建数据", nickname = "createContent") 自动生成 Client Swagger Codegen, 官方 Usage Doc 在让业务方使用我们接口的时候,自然而然的我们需要提供对应的 Client,如果一个个手动封装接口,会带来很多的重复工作,维护起来也很麻烦,于是我们想寻求一些自动生成的方案。后来发现 Swagger 官方已经帮我们想好了,对应的就是 Swagger Codegen,它的原理也不复杂,就是基于 Swagger 的接口描述文件生成 Client 代码,当然它也支持生成 Server 端的项目骨架。 下面,我就详细说下我们的使用经验。 多模块项目生成 Client 针对多模块项目,我们会期望 Client 的 pom 可以自定义,因为 client 会依赖 common 的一些 Model 类,另外会依赖 Parent Pom,所以不能通过 codegen 生成。这个可以通过 .swagger-codegen-ignore 实现,该文件你可以理解为类似 .gitignore 的文件,在 codegen 的时候会忽略生成该文件下的文件列表。 .swagger-codegen-ignore 要放到 client 目录下,配置的路径都是相对 Client 目录而言,你可以把所有不想生成的文件都列在里面,下面就是一个具体示例: build.sh build.sbt build.gradle gradle.properties gradlew gradlew.bat pom.xml README.md settings.gradle .gitignore gradle .swagger-codegen/VERSION docs/** git_push.sh .travis.yml src/main/AndroidManifest.xml 推荐通过 maven 插件配置生成 Client。 对应的插件配置示例如下: <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>clean-additional-generated-files</id> <phase>generate-sources</phase> <goals> <goal>clean</goal> </goals> <configuration> <excludeDefaultDirectories>true</excludeDefaultDirectories> <filesets> <fileset> <directory>${project.basedir}/src/main/java</directory> <!--<directory>${project.basedir}/src/test</directory>--> </fileset> </filesets> </configuration> </execution> </executions> </plugin> <plugin> <groupId>io.swagger</groupId> <artifactId>swagger-codegen-maven-plugin</artifactId> <version>2.3.1</version> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <templateDirectory>${project.basedir}/src/main/resources/template</templateDirectory> <inputSpec>${project.basedir}/src/main/resources/api.json</inputSpec> <!--<inputSpec>http://localhost:7001/v2/api-docs?group=1.0</inputSpec>--> <language>java</language> <output>${project.basedir}</output> <importMappings> <importMapping>ContentInfo=com.tmall.pegasus.dms.common.result.ContentInfo</importMapping> <importMapping>DataContent=com.tmall.pegasus.dms.common.params.DataContent</importMapping> <importMapping>DeliveryInfo=com.tmall.pegasus.dms.common.result.DeliveryInfo</importMapping> <importMapping>FieldInfo=com.tmall.pegasus.dms.common.result.FieldInfo</importMapping> <importMapping>ResourceInfo=com.tmall.pegasus.dms.common.result.ResourceInfo</importMapping> </importMappings> <invokerPackage>com.tmall.pegasus.dms.client</invokerPackage> <apiPackage>com.tmall.pegasus.dms.client.api</apiPackage> <modelPackage>com.tmall.pegasus.dms.common.result</modelPackage> <library>feign</library> <configOptions> <sourceFolder>src/main/java</sourceFolder> <ignoreFileOverride>${project.basedir}/.swagger-codegen-ignore</ignoreFileOverride> </configOptions> </configuration> </execution> </executions> </plugin> 第一个插件是 maven-clean-plugin,它帮助我们 clean 掉上次生成的代码,第二个就是 maven-swagger-codegen 插件,可以看出里面的配置非常丰富,各个配置的含义都可以在 官方文档 找到,有几个配置我觉得会常用到,下面简单介绍下。 importMappings 当你不想用 Swagger Codegen 自己生成的 Model 类,而是想用自己 Common 包里的 Model 时,你可以用该配置实现,配置项就是一个个的映射关系,在 Codegen 的时候会将 Model 引用替换成你设置的值。 apiPackage 就是你接口的包名 library Codegen 默认支持多种 http client ,你可以通过该参数指定。 ignoreFileOverride 就是 .swagger-codegen-ignore 对应的路径。 templateDirectory 你如果想自定义生成 Client 代码,可以通过自定义 Generator 实现,你如果只是想对系统生成的 Client 做微小调整,可以通过修改系统自带的的 Template 实现。方法就是把 Swaggen Codegen 的模板文件拷贝一份到你的 src/main/resources 里,直接改里面的文件,同时记得配置 templateDirectory 的路径,再重新生成即可。 将生成文件均放到 .gitignore 中。这样的好处是可以避免大量无意义的提交其他 ==== 本文更多介绍的是这套方案中我们关注的点,并没有一步步的介绍开发步骤,因为这方面的资料谷歌上有很多,大家可以自行查阅。如果阅读中有不理解或疑惑的点,欢迎钉钉 @澶渊 、@乱我。 另外说点自己开发的一些感想,在开发的过程中,应尽可能的让自己变的 “懒” 一些,要对 “重复” 保持足够的敏感,每当感觉有重复代码出现时,就要考虑这里是否可以复用,是否可以通过 AOP 实现,是否可以自动生成,总的来说就是用更少的代码做更多的事,最后谢谢阅读本文。

优秀的个人博客,低调大师

WEB API已成为构建客户端服务的利器?

一、软件开发的互联网思维 1.网络就是计算机 进入互联网时代,软件开发必然放眼全局,充分利用互联网的资源和特性。比如,为了提高响应性能,就选择使用CDN作为前端缓存。而且可能还需要采用CDN提供的API进行编程,以便更加优化地调度缓存资源。类似这样可利用的网络资源很多,开发者需要统一考虑到,也就是建立起软件开发的互联网思维。这样开发出来的软件,才具有互联网基因,能够适应互联网的时代。 2. 云计算助力软件开发 云计算通过虚拟化技术把很多资源整合起来,软件开发理念不断向更高维度抽象。从虚拟机到集群,再到容器,再抽象成函数计算,软件开发越来越聚焦在业务核心上。这样的演进,必然带来效率的提升。基于云计算环境的软件,不可避免地需要通过各种API来进行资源管理。 3. WEB API的作用 1)开放有价值的数据 一些数据具有较强的

优秀的个人博客,低调大师

开源中国iOS客户端学习——(三)再看协议与委托

iOS里委托与协议是很重要的一块,如果理解不好很难区分协议与委托到底有什么不一样,这些东西在开发中是经常遇见的; 协议是类留给外部的一个接口函数的集合(一位高手用C++基类来解释是,把接口做为参数,回调基类的函数时,运行时识别,调用了相应子类的成员函数); 委托是iOS一种设计模式,通过委托别的类,来调用协议里的方法,相当于一个回调过程; 使用理解委托与协议步骤: (1) 弄清楚谁委托谁,需要干什么? 拿上一篇博客下拉刷新源码案例分析,(在源码工程中comment文件夹里)有MessageSystemView这样一个类,该类用于用户的信息所用(比如谁给你留言,或者评论了你某一篇博客),我们也需要刷新获取最新信息,此处用到下拉刷新第三方类库EGORefreshTableHeaderView类委托MessageSystemView类来实现这个特性,这就需要在EGORefreshTableHeaderView类定义一个协议对象delegate。 (2)被委托类需要在interface中声明<XXDelegate>,表示该类要实现协议里的方法. MessageSystemView实现EGORefreshTableHeaderView协议方法就要遵循EGORefreshTableHeaderDelegate协议. (3)在被委托类里定义一个委托类的对象,将xx.delegate=self,意思是把委托的对象只想被委托对象; 在EGORefreshTableHeaderView.h文件中 EGORefreshTableHeaderView*_refreshHeaderView; 在ViewDidLoad中 view.delegate = self; _refreshHeaderView = view; - (void)viewDidLoad { allCount = 0; [super viewDidLoad]; if (self.tabTitle) { self.tabBarItem.title = self.tabTitle; } //加载固定数据 imageDownloadsInProgress = [NSMutableDictionary dictionary]; comments = [[NSMutableArray alloc] initWithCapacity:10]; [self reload:YES]; //添加的代码 if (_refreshHeaderView == nil) { EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, -320.0f, self.view.frame.size.width, 320)]; view.delegate = self; [self.tableComments addSubview:view]; _refreshHeaderView = view; } [_refreshHeaderView refreshLastUpdatedDate]; self.tableComments.backgroundColor = [UIColor colorWithRed:248.0/255.0 green:249.0/255.0 blue:249.0/255.0 alpha:1.0]; } (4)在被委托类的实现文件中调用协议方法,这就是实现委托方法的一个过程; 此处EGORefreshTableHeaderView类对象委托MessageSystemView类对象,在MessageSystemView实现对EGORefreshTableHeaderView类协议的调用; MessageSystemView.m文件实现委托方法 - (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view { [self reloadTableViewDataSource]; [self refresh]; } - (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view { return _reloading; } - (NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view { return [NSDate date]; } 当然要实现完整的下拉功能这几个委托只是其中一部分,被委托类里还需要有自己的成员函数,在委托方法正在被调用的时候触发被委托成员函数方法,在 MessageSystemView.h //下拉刷新 - (void)refresh;//解析网络数据,让数据显示到视图上 - (void)reloadTableViewDataSource;//开始加载时调用此方法 - (void)doneLoadingTableViewData;//完成加载时调用此方法 MessageSystemView.m #pragma 下提刷新 - (void)reloadTableViewDataSource { _reloading = YES; } - (void)doneLoadingTableViewData { _reloading = NO; [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.tableComments]; } - (void)scrollViewDidScroll:(UIScrollView *)scrollView { [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView]; } - (void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view { [self reloadTableViewDataSource]; [self refresh]; } - (BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view { return _reloading; } - (NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view { return [NSDate date]; } - (void)refresh { isLoadOver = NO; [self reload:NO]; } 根据这些练习了一个简单的下拉刷新Demo 效果图 .h文件里 #import <UIKit/UIKit.h> #import "EGORefreshTableHeaderView.h" @interface ViewController : UIViewController<UIScrollViewDelegate,UITableViewDelegate,UITableViewDataSource, EGORefreshTableHeaderDelegate> { EGORefreshTableHeaderView *_refreshHeaderView; BOOL _reloading; } @property (retain, nonatomic) IBOutlet UITableView *tableView; -(void)reloadTableViewDataSource; -(void)doneLoadingTableViewData; @end .m文件里 - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. if (_refreshHeaderView == nil) { EGORefreshTableHeaderView *view = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, -self.tableView.frame.size.height, self.view.frame.size.width, self.tableView.frame.size.height)]; view.delegate = self; [self.tableView addSubview:view]; _refreshHeaderView = view; [view release]; } [_refreshHeaderView refreshLastUpdatedDate]; } //重新加载时调用 -(void)reloadTableViewDataSource { _reloading = YES; } //完成加载时调用 -(void)doneLoadingTableViewData { _reloading = NO; [_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.tableView]; } #pragma mark - #pragma mark UIScrollViewDelegate Methods -(void)scrollViewDidScroll:(UIScrollView *)scrollView { [_refreshHeaderView egoRefreshScrollViewDidScroll:scrollView]; } -(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { [_refreshHeaderView egoRefreshScrollViewDidEndDragging:scrollView]; } #pragma mark - #pragma mark EGORefreshTableHeaderDelegate Methods -(void)egoRefreshTableHeaderDidTriggerRefresh:(EGORefreshTableHeaderView *)view { [self reloadTableViewDataSource]; [self performSelector:@selector(doneLoadingTableViewData) withObject:nil afterDelay:3.0]; } -(BOOL)egoRefreshTableHeaderDataSourceIsLoading:(EGORefreshTableHeaderView *)view { return _reloading; } -(NSDate *)egoRefreshTableHeaderDataSourceLastUpdated:(EGORefreshTableHeaderView *)view { return [NSDate date]; } #pragma mark - #pragma mark UITableViewDataSource -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 3; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell==nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } cell.textLabel.text = @"下拉刷新测试"; return cell; } 源码:http://download.csdn.net/detail/duxinfeng2010/4902782 正在学习过程中,错误之处请指正,欢迎交流,共同学习; 欢迎转载分享,请注明出处http://blog.csdn.net/duxinfeng2010 本文转自新风作浪 51CTO博客,原文链接http://blog.51cto.com/duxinfeng/1208693:,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册