ANTLR教程– Hello Word

Antlr代表另一种语言识别工具。 该工具能够为任何计算机语言生成编译器或解释器。 除了明显的用途(例如需要解析一种真正的“大型”编程语言,例如Java,PHP或SQL)外,它还可以帮助执行更小,更常见的任务。

每当您需要评估编译时未知的表达式,或者解析奇怪的用户输入或文件时,这都是有用的。 当然,可以为任何这些任务创建定制的手工分析器。 但是,通常需要更多时间和精力。 对良好的解析器生成器的一点了解可能会将这些耗时的任务变成简单而又快速的练习。

这篇文章从ANTLR有用性的一个小例子开始。 然后,我们解释什么是ANTLR以及它如何工作。 最后,我们展示如何编译一个简单的“ Hello word!” 语言转换成抽象语法树。 文章还说明了如何添加错误处理以及如何测试语言。

下一篇文章展示了如何创建一种真实的表达语言。

实词示例

ANTLR在开源单词中似乎很流行。 其中, Apache Camel , Apache Lucene , Apache Hadoop , Groovy和Hibernate都使用它 。 他们都需要用于自定义语言的解析器。 例如,Hibernate使用ANTLR解析其查询语言HQL。

所有这些都是大型框架,因此与小型应用程序相比,它们更可能需要特定领域的语言。 使用ANTLR的较小项目的列表可在其展示列表中找到 。 我们还找到了一个关于该主题的stackoverflow讨论。

若要查看ANTLR可能在哪里有用以及如何节省时间,请尝试估算以下要求:

  • 将公式计算器添加到会计系统中。 它将计算公式的值,例如(10 + 80)*sales_tax
  • 将扩展的搜索字段添加到配方搜索引擎中。 它将搜索匹配表达式的收据,例如(chicken and orange) or (no meat and carrot)

我们的安全评估需要一天半的时间,其中包括文档,测试以及与项目的集成。 如果您面临类似的要求并且做出了更高的估计,那么ANTLR值得一看。

总览

ANTLR是代码生成器。 它以所谓的语法文件作为输入,并生成两个类:lexer和parser。

Lexer首先运行,然后将输入分成称为令牌的部分。 每个令牌代表或多或少有意义的输入。 代币流被传递到解析器,解析器完成所有必要的工作。 解析器负责构建抽象语法树,解释代码或将其转换为其他形式。

语法文件包含ANTLR生成正确的词法分析器和解析器所需的所有内容。 它是否应该生成Java或python类,解析器是否生成抽象语法树,汇编代码或直接解释代码等。 正如本教程显示如何构建抽象语法树一样,在以下说明中我们将忽略其他选项。

最重要的是,语法文件描述了如何将输入分为令牌以及如何从令牌构建树。 换句话说,语法文件包含词法分析器规则和解析器规则。

每个词法分析器规则描述一个令牌:

TokenName: regular expression;

解析器规则更加复杂。 最基本的版本类似于lexer规则中的版本:

ParserRuleName: regular expression;

它们可能包含修饰符,这些修饰符在结果抽象语法树中指定输入,根和子元素上的特殊转换,或在使用规则时执行的操作。 几乎所有工作通常都在解析器规则内完成。

基础设施

首先,我们展示使ANTLR开发更容易的工具。 当然,本章中所描述的内容都不是必需的。 所有示例仅适用于maven,文本编辑器和Internet连接。

ANTLR项目制作了独立的IDE , Eclipse插件和Idea插件 。 我们没有找到NetBeans插件。

ANTLRWorks

独立的想法称为ANTLRWorks 。 从项目下载页面下载 。 ANTLRWorks是单个jar文件,请使用java -jar antlrworks-1.4.3.jar命令运行它。

IDE具有更多功能,并且比Eclipse插件更稳定。

Eclipse插件

从ANTLR 下载页面下载并解压缩ANTLR v3。 然后,从Eclipse Marketplace安装ANTLR插件:

转到“首选项”并配置ANTLR v3安装目录:

要测试配置,请下载示例语法文件并在eclipse中打开它。 它将在ANTLR编辑器中打开。 编辑器具有三个选项卡:

  • 语法–具有语法突出显示,代码完成等功能的文本编辑器。
  • 解释器–将测试表达式编译成语法树,可能会产生与生成的解析器不同的结果。 它倾向于在正确的表达式上抛出失败的谓词异常。
  • 铁路视图–绘制词法分析器和解析器规则的漂亮图形。

空项目– Maven配置

本章说明如何将ANTLR添加到Maven项目中。 如果您使用Eclipse且尚未安装m2eclipse插件,请从http://download.eclipse.org/technology/m2e/releases更新站点进行安装。 这将使您的生活更加轻松。

建立专案

创建一个新的Maven项目,并在“选择原型”屏幕上指定maven-archetype-quickstart。 如果不使用Eclipse,则命令mvn archetype:generate可以达到相同的效果。

相依性

将ANTLR依赖项添加到pom.xml中 :

org.antlrantlr3.3jarcompile

注意:由于ANTLR没有向后兼容的历史记录,因此最好指定所需的版本。

外挂程式

Antlr maven插件在generate-sources阶段运行,并从语法(.g)文件生成lexer和parser java类。 将其添加到pom.xml中 :

org.antlrantlr3-maven-plugin3.3run antlrgenerate-sourcesantlr

创建src/main/antlr3文件夹。 该插件希望其中包含所有语法文件。

生成的文件放在target/generated-sources/antlr3目录中。 由于此目录不在默认的maven构建路径中,因此我们使用build-helper-maven-plugin将其添加到该目录中:

org.codehaus.mojobuild-helper-maven-pluginadd-sourcegenerate-sourcesadd-source${basedir}/target/generated-sources/antlr3

如果使用eclipse,则必须更新项目配置:右键单击项目->'maven'->'更新项目配置'。

测试一下

调用maven以测试项目配置:右键单击项目->'Run As'->'Maven generate-sources'。 或者,使用mvn generate-sources命令。

构建应该成功。 控制台输出应包含antlr3-maven-plugin插件输出:

[INFO] --- antlr3-maven-plugin:3.3:antlr (run antlr) @ antlr-step-by-step ---
[INFO] ANTLR: Processing source directory C:\meri\ANTLR\workspace\antlr-step-by-step\src\main\antlr3
[INFO] No grammars to process
ANTLR Parser Generator  Version 3.3 Nov 30, 2010 12:46:29

它之后应该是build-helper-maven-plugin插件输出:

[INFO] --- build-helper-maven-plugin:1.7:add-source (add-source) @ antlr-step-by-step ---
[INFO] Source directory: C:\meri\ANTLR\workspace\antlr-step-by-step\target\generated-sources\antlr3 added.

此阶段的结果位于github上,标记为001-configured_antlr 。

你好字

我们将创建最简单的语言解析器– hello word解析器。 它通过一个表达式构建一个小的抽象语法树:“ Hello word!”。

我们将使用它来显示如何创建语法文件并从中生成ANTLR类。 然后,我们将展示如何使用生成的文件并创建单元测试。

第一个语法文件

Antlr3-maven-plugin在src/main/antlr3目录中搜索语法文件。 它使用语法为每个子目录创建新程序包,并在其中生成解析器和词法分析器类。 由于我们希望将类生成到org.meri.antlr_step_by_step.parsers包中,因此我们必须创建src/main/antlr3/org/meri/antlr_step_by_step/parsers目录。

语法名称和文件名必须相同。 文件必须带有.g后缀。 此外,每个语法文件都以语法名称声明开头。 我们的S001HelloWord语法从以下几行开始:

grammar S001HelloWord;

声明之后始终是生成器选项。 我们正在研究Java项目,希望将表达式编译成抽象语法树:

options {// antlr will generate java lexer and parserlanguage = Java;// generated parser should create abstract syntax treeoutput = AST;
}

Antlr不会在生成的类之上生成包声明。 我们必须使用@parser::header@lexer::header块来实施它。 标头必须遵循选项块:

@lexer::header {package org.meri.antlr_step_by_step.parsers;
}@parser::header {package org.meri.antlr_step_by_step.parsers;
}

每个语法文件必须至少具有一个词法分析器规则。 每个词法分析器规则必须以大写字母开头。 我们有两个规则,第一个定义一个称呼令牌,第二个定义一个endsymbol令牌。 称呼必须为“ Hello word”,并且结尾符号必须为“!”。

SALUTATION:'Hello word';   
ENDSYMBOL:'!';

同样,每个语法文件必须至少具有一个解析器规则。 每个解析器规则必须以小写字母开头。 我们只有一个解析器规则:我们语言中的任何表达式都必须由称呼后跟一个结尾符号组成。

expression : SALUTATION ENDSYMBOL;

注意:语法文件元素的顺序是固定的。 如果更改它,则antlr插件将失败。

生成词法分析器和解析器

使用mvn generate-sources命令或从Eclipse从命令行生成词法分析器和解析器:

  • 右键单击该项目。
  • 点击“运行方式”。
  • 单击“ Maven生成源”。

Antlr插件将创建target / generated-sources / antlr / org / meri / antlr_step_by_step / parsers文件夹,并将S001HelloWordLexer.java和S001HelloWordParser.java文件放入其中。

使用Lexer和Parser

最后,我们创建编译器类。 它只有一种公共方法,该方法:

  • 调用生成的词法分析器将输入拆分为令牌,
  • 调用生成的解析器以根据令牌构建AST,
  • 将结果AST树打印到控制台中,
  • 返回抽象语法树。

编译器位于S001HelloWordCompiler类中:

public CommonTree compile(String expression) {try {//lexer splits input into tokensANTLRStringStream input = new ANTLRStringStream(expression);TokenStream tokens = new CommonTokenStream( new S001HelloWordLexer( input ) );//parser generates abstract syntax treeS001HelloWordParser parser = new S001HelloWordParser(tokens);S001HelloWordParser.expression_return ret = parser.expression();//acquire parse resultCommonTree ast = (CommonTree) ret.tree;printTree(ast);return ast;} catch (RecognitionException e) {throw new IllegalStateException("Recognition exception is never thrown, only declared.");}

注意:不必担心在S001HelloWordParser.expression()方法上声明的RecognitionException异常。 它永远不会被抛出。

测试它

在本章结束时,我们将使用一个针对新编译器的小测试用例。 创建S001HelloWordTest类:

public class S001HelloWordTest {/*** Abstract syntax tree generated from "Hello word!" should have an * unnamed root node with two children. First child corresponds to * salutation token and second child corresponds to end symbol token.* * Token type constants are defined in generated S001HelloWordParser * class.*/@Testpublic void testCorrectExpression() {//compile the expressionS001HelloWordCompiler compiler = new S001HelloWordCompiler();CommonTree ast = compiler.compile("Hello word!");CommonTree leftChild = ast.getChild(0);CommonTree rightChild = ast.getChild(1);//check ast structureassertEquals(S001HelloWordParser.SALUTATION, leftChild.getType());assertEquals(S001HelloWordParser.ENDSYMBOL, rightChild.getType());}}

测试将成功通过。 它将抽象语法树打印到控制台:

0 null-- 4 Hello word-- 5 !

IDE中的语法

在编辑器中打开S001HelloWord.g并进入解释器选项卡。

  • 在左上方视图中突出显示表达式规则。
  • 写下“你好字!” 进入右上视图。
  • 按左上角的绿色箭头。

解释器将生成解析树:

复制语法

本教程中的每个新语法都基于先前的语法。 我们汇总了将旧语法复制到新语法所需的步骤列表。 使用它们将OldGrammar复制到NewGrammar:

  • 将OldGrammar.g复制到同一目录中的NewGrammar.g 。
  • 将语法声明更改为grammar NewGrammar;
  • 生成解析器和词法分析器。
  • 创建类似于先前的OldGrammarCompiler类的新类NewGrammarCompiler 。
  • 创建类似于先前的OldGrammarTest类的新测试类NewGrammarTest 。

错误处理

没有适当的错误处理,没有任何任务真正完成。 生成的ANTLR类尽可能尝试从错误中恢复。 它们的确向控制台报告错误,但是没有现成的API可以以编程方式查找语法错误。

如果我们只构建命令行编译器,那可能很好。 但是,假设我们正在为我们的语言构建GUI,或将结果用作其他工具的输入。 在这种情况下,我们需要对所有生成的错误进行API访问。

在本章的开始,我们将尝试默认错误处理并为其创建测试用例。 然后,我们将添加一个简单的错误处理,只要发生第一个错误,该处理都会引发异常。 最后,我们将转到“真实”解决方案。 它将在内部列表中收集所有错误并提供访问它们的方法。

作为副产品,本章介绍了如何:

  • 将自定义catch子句添加到解析器规则中 ,
  • 向生成的类中添加新的方法和字段 ,
  • 覆盖生成的方法 。

默认错误处理

首先,我们将尝试解析各种不正确的表达式。 目的是了解默认的ANTLR错误处理行为。 我们将从每个实验中创建测试用例。 所有测试用例都位于S001HelloWordExperimentsTest类中。

表达式1Hello word?

结果树与正确的树非常相似:

0 null-- 4 Hello word-- 5 ?<missing ENDSYMBOL>

控制台输出包含错误:

line 1:10 no viable alternative at character '?'
line 1:11 missing ENDSYMBOL at '<eof>'

测试用例 :以下测试用例通过均没有问题。 不会引发异常,并且抽象语法树节点类型与正确的表达式中的相同。

@Testpublic void testSmallError() {//compile the expressionS001HelloWordCompiler compiler = new S001HelloWordCompiler();CommonTree ast = compiler.compile("Hello word?");//check AST structureassertEquals(S001HelloWordParser.SALUTATION, ast.getChild(0).getType());assertEquals(S001HelloWordParser.ENDSYMBOL, ast.getChild(1).getType());}

表情2Bye!

结果树与正确的树非常相似:

0 null-- 4 <missing>-- 5 !</missing>

控制台输出包含错误:

line 1:0 no viable alternative at character 'B'
line 1:1 no viable alternative at character 'y'
line 1:2 no viable alternative at character 'e'
line 1:3 missing SALUTATION at '!'

测试用例 :以下测试用例通过均没有问题。 不会引发异常,并且抽象语法树节点类型与正确的表达式中的相同。

@Testpublic void testBiggerError() {//compile the expressionS001HelloWordCompiler compiler = new S001HelloWordCompiler();CommonTree ast = compiler.compile("Bye!");//check AST structureassertEquals(S001HelloWordParser.SALUTATION, ast.getChild(0).getType());assertEquals(S001HelloWordParser.ENDSYMBOL, ast.getChild(1).getType());}

表达式3Incorrect Expression

结果树只有根节点,没有子节点:

0

控制台输出包含很多错误:

line 1:0 no viable alternative at character 'I'
line 1:1 no viable alternative at character 'n'
line 1:2 no viable alternative at character 'c'
line 1:3 no viable alternative at character 'o'
line 1:4 no viable alternative at character 'r'
line 1:5 no viable alternative at character 'r'
line 1:6 no viable alternative at character 'e'
line 1:7 no viable alternative at character 'c'
line 1:8 no viable alternative at character 't'
line 1:9 no viable alternative at character ' '
line 1:10 no viable alternative at character 'E'
line 1:11 no viable alternative at character 'x'
line 1:12 no viable alternative at character 'p'
line 1:13 no viable alternative at character 'r'
line 1:14 no viable alternative at character 'e'
line 1:15 no viable alternative at character 's'
line 1:16 no viable alternative at character 's'
line 1:17 no viable alternative at character 'i'
line 1:18 no viable alternative at character 'o'
line 1:19 no viable alternative at character 'n'
line 1:20 mismatched input '&ltEOF>' expecting SALUTATION

测试用例 :我们终于找到一个表达式,该表达式导致不同的树结构。

@Testpublic void testCompletelyWrong() {//compile the expressionS001HelloWordCompiler compiler = new S001HelloWordCompiler();CommonTree ast = compiler.compile("Incorrect Expression");//check AST structureassertEquals(0, ast.getChildCount());}

Lexer中的错误处理

每个词法分析器规则“ RULE”对应于生成的词法分析器中的“ mRULE”方法。 例如,我们的语法有两个规则:

SALUTATION:'Hello word';   
ENDSYMBOL:'!';

并且生成的词法分析器有两种相应的方法 :

public final void mSALUTATION() throws RecognitionException {// ...
}public final void mENDSYMBOL() throws RecognitionException {// ...
}

根据抛出的异常,lexer可能会也可能不会尝试从中恢复。 但是,每个错误都以reportError(RecognitionException e)方法结尾。 生成的词法分析器继承它:

public void reportError(RecognitionException e) {displayRecognitionError(this.getTokenNames(), e);}

结果:我们必须在lexer中更改reportError或displayRecognitionError方法。

解析器中的错误处理

我们的语法只有一个解析器规则“表达式”:

expression SALUTATION ENDSYMBOL;

该表达式对应于生成的解析器中的expression()方法:

public final expression_return expression() throws RecognitionException {//initializationtry {//parsing}catch (RecognitionException re) {reportError(re);recover(input,re);retval.tree = (Object) adaptor.errorNode(input, retval.start, input.LT(-1), re);} finally {}//return result;
}

如果发生错误,解析器将:

  • 向控制台报告错误,
  • 从错误中恢复
  • 将错误节点(而不是普通节点)添加到抽象语法树。

解析器中的错误报告比lexer中的错误报告稍微复杂一些:

/** Report a recognition problem.**  This method sets errorRecovery to indicate the parser is recovering*  not parsing.  Once in recovery mode, no errors are generated.*  To get out of recovery mode, the parser must successfully match*  a token (after a resync).  So it will go:**   1. error occurs*   2. enter recovery mode, report error*   3. consume until token found in resynch set*   4. try to resume parsing*   5. next match() will reset errorRecovery mode**  If you override, make sure to update syntaxErrors if you care about that.*/public void reportError(RecognitionException e) {// if we've already reported an error and have not matched a token// yet successfully, don't report any errors.if ( state.errorRecovery ) {return;}state.syntaxErrors++; // don't count spuriousstate.errorRecovery = true;displayRecognitionError(this.getTokenNames(), e);}

这次我们有两个可能的选择:

  • 通过自己的处理替换解析器规则方法中的catch子句,
  • 覆盖解析器方法。

在解析器中更改捕获

Antlr提供了两种方法来更改解析器中生成的catch子句。 我们将创建两个新的语法,每个语法都演示一种方法。 在这两种情况下,我们都会使解析器在第一个错误时退出。

首先,我们可以将rulecatch添加到新S002HelloWordWithErrorHandling语法的解析器规则中:

expression : SALUTATION ENDSYMBOL;
catch [RecognitionException e] {//Custom handling of an exception. Any java code is allowed.throw new S002HelloWordError(":(", e);
}

当然,我们必须将S002HelloWordError异常的导入添加到headers块中 :

@parser::header {package org.meri.antlr_step_by_step.parsers;//add imports (see full line on Github)import ... .S002HelloWordWithErrorHandlingCompiler.S002HelloWordError;
}

编译器类与以前几乎相同。 它声明了新的异常:

public class S002HelloWordWithErrorHandlingCompiler extends AbstractCompiler {public CommonTree compile(String expression) {// no change here}@SuppressWarnings("serial")public static class S002HelloWordError extends RuntimeException {public S002HelloWordError(String arg0, Throwable arg1) {super(arg0, arg1);}}
}

然后,ANTLR将用我们自己的处理方式替换表达式规则方法中的默认catch子句:

public final expression_return expression() throws RecognitionException {//initializationtry {//parsing}catch (RecognitionException re) {//Custom handling of an exception. Any java code is allowed.throw new S002HelloWordError(":(", e); } finally {}//return result;
}

通常, 语法 , 编译器类和测试类在Github上都可用。

或者,我们可以将rulecatch规则放在标题块和第一个lexer规则之间。 S003HelloWordWithErrorHandling语法演示了此方法:

//change error handling in all parser rules
@rulecatch {catch (RecognitionException e) {//Custom handling of an exception. Any java code is allowed.throw new S003HelloWordError(":(", e);}
}

我们必须将S003HelloWordError异常的导入添加到标头块中:

@parser::header {package org.meri.antlr_step_by_step.parsers;//add imports (see full line on Github)import ... .S003HelloWordWithErrorHandlingCompiler.S003HelloWordError;
}

编译器类与前面的情况完全相同。 ANTLR将替换所有解析器规则中的默认catch子句:

public final expression_return expression() throws RecognitionException {//initializationtry {//parsing}catch (RecognitionException re) {//Custom handling of an exception. Any java code is allowed.throw new S003HelloWordError(":(", e); } finally {}//return result;
}

同样,Github提供了语法 , 编译器类和测试类 。

不幸的是,这种方法有两个缺点。 首先,它仅在解析器中不适用于lexer。 其次,默认报告和恢复功能以合理的方式工作。 它尝试从错误中恢复。 一旦开始恢复,就不会产生新的错误。 仅当解析器未处于错误恢复模式时,才会生成错误消息。

我们喜欢此功能,因此我们决定仅更改错误报告的默认实现。


将方法和字段添加到生成的类

我们会将所有词法分析器/解析器错误存储在私有列表中。 此外,我们将在生成的类中添加两个方法:

  • hasErrors –如果发生至少一个错误,则返回true,
  • getErrors –返回所有生成的错误。

在@members块内添加了新的字段和方法:

@lexer::members {//everything you need to add to the lexer
}@parser::members {//everything you need to add to the parser
}

成员块必须放在标题块和第一个词法分析器规则之间。 该示例的语法为S004HelloWordWithErrorHandling :

//add new members to generated lexer
@lexer::members {//add new fieldprivate List<RecognitionException> errors = new ArrayList <RecognitionException> ();//add new methodpublic List<RecognitionException> getAllErrors() {return new ArrayList<RecognitionException>(errors);}//add new methodpublic boolean hasErrors() {return !errors.isEmpty();}
}//add new members to generated parser
@parser::members {//add new fieldprivate List<RecognitionException> errors = new ArrayList <RecognitionException> ();//add new methodpublic List<RecognitionException> getAllErrors() {return new ArrayList<RecognitionException>(errors);}//add new methodpublic boolean hasErrors() {return !errors.isEmpty();}
}

生成的词法分析器和生成的解析器都包含用member块编写的所有字段和方法。

覆盖生成的方法

要覆盖生成的方法,请执行与要添加新方法相同的操作,例如,将其添加到@members块中:

//override generated method in lexer
@lexer::members {//override methodpublic void reportError(RecognitionException e) {errors.add(e);displayRecognitionError(this.getTokenNames(), e);}
}//override generated method in parser
@parser::members {//override methodpublic void reportError(RecognitionException e) {errors.add(e);displayRecognitionError(this.getTokenNames(), e);}
}

现在,reportError方法将覆盖lexer和parser中的默认行为。

收集编译器中的错误

最后,我们必须更改编译器类。 新版本将在输入解析阶段之后收集所有错误:

private List<RecognitionException> errors = new ArrayList<RecognitionException>();public CommonTree compile(String expression) {try {... init lexer ...... init parser ...ret = parser.expression();//collect all errorsif (lexer.hasErrors())errors.addAll(lexer.getAllErrors());if (parser.hasErrors())errors.addAll(parser.getAllErrors());//acquire parse result... as usually ...} catch (RecognitionException e) {...}
}/**
* @return all errors found during last run
*/
public List<RecognitionException> getAllErrors() {return errors;
}

解析器完成工作后,我们必须收集词法分析器错误。 从它调用词法分析器,之前没有任何错误。 像往常一样,我们将语法 , 编译器类和测试类放在Github上。

下载antlr分步项目的标记003-S002-to-S004HelloWordWithErrorHandling ,以查找同一java项目中的所有三种错误处理方法。

参考: ANTLR教程–我们的JCG合作伙伴 Maria Jurcovicova在This is Stuff博客上的问候语。


翻译自: https://www.javacodegeeks.com/2012/04/antlr-tutorial-hello-word.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/372723.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centOS 6.5安装python和nginx

一、安装python3.5 1、安装python3.5 2、安装pip并升级到最新 下载wget --no-check-certificate https://github.com/pypa/pip/archive/1.5.5.tar.gz 注意&#xff1a;wget获取https的时候要加上&#xff1a;--no-check-certificate tar zvxf 1.5.5.tar.gz #解压文件 cd pip…

插件化编程实现的一份糖炒栗子~~

迷茫的原因是因为想得太多&#xff0c;做得太少。因为只是 想 真的很容易&#xff0c;转瞬之间就会产生无数个念头&#xff0c;或许是该做点什么了吧。 但是整个人都是懒的&#xff0c;是废的&#xff0c;是大脑控制不住自己的行为的。解决方案唯有一步一步的去把行为变成习惯。…

用C#来学习唐诗三百首和全唐诗

Begin 最近把项目做完了&#xff0c;闲来无事&#xff0c;就想做点好玩的事情&#xff0c;刚好前几天下载了【唐诗三百首】和【全唐诗】这两个txt文件&#xff0c;正好用C#来整理一下。 然后导出QData格式&#xff0c;可以给其他软件读取。 以后弄个开机自动显示一句诗&#xf…

sts java配置tomcat_STS配置Tomcat.9.0

今天&#xff0c;心血来潮&#xff0c;弄了一下STS,按着建立WEB项目的方式建立工程。一、新建工程(FILE --NEW--Dynamic Web project)二、输入项目名称&#xff0c;TestWeb&#xff0c;然后下一步&#xff0c;点击FInish.三、新建index.jsp并打开index.jsp,书写测试成功&#x…

javaweb国际化

根据数据的类型不同&#xff0c;国际化分为2类&#xff1a;静态数据国际化和动态数据的国际化。 静态数据&#xff0c;包括 “标题”、“用户名”、“密码”这样的文字数据。 动态数据&#xff0c;包括日期、货币等可以动态生成的数据。 国际化涉及到java.util.Locale和java.ut…

20145335郝昊《网络攻防》Bof逆向基础——ShellCode注入与执行

20145335郝昊《网络攻防》Bof逆向基础——ShellCode注入与执行 实验原理 关于ShellCode&#xff1a;ShellCode是一段代码&#xff0c;作为数据发送给受攻击服务器&#xff0c;是溢出程序和蠕虫病毒的核心&#xff0c;一般可以获取权限。我们将代码存储到对方的堆栈中&#xff0…

玩转Android之加速度传感器的使用,模仿微信摇一摇

Android系统带的传感器有很多种&#xff0c;最常见的莫过于微信的摇一摇了&#xff0c;那么今天我们就来看看Anroid中传感器的使用&#xff0c;做一个类似于微信摇一摇的效果。 OK ,废话不多说&#xff0c;我们就先来看看效果图吧&#xff1a; 当我摇动手机的时候这里的动画效果…

Java两种设计模式_23种设计模式(11)java策略模式

23种设计模式第四篇&#xff1a;java策略模式定义&#xff1a;定义一组算法&#xff0c;将每个算法都封装起来&#xff0c;并且使他们之间可以互换。类型&#xff1a;行为类模式类图&#xff1a;策略模式是对算法的封装&#xff0c;把一系列的算法分别封装到对应的类中&#xf…

JAXB自定义绑定– Java.util.Date / Spring 3序列化

JaxB可以处理Java.util.Date序列化&#xff0c;但是需要以下格式&#xff1a; “ yyyy-MM-ddTHH&#xff1a;mm&#xff1a;ss ”。 如果需要将日期对象格式化为另一种格式怎么办&#xff1f; 我有同样的问题时&#xff0c;我正在同春MVC 3和Jackson JSON处理器 &#xff0c;最…

双足机器人简单步态生成

让机器人行走最简单的方法是先得到一组步态曲线&#xff0c;即腿部每个关节随时间运动的角度值。可以在ADAMS或3D Max、Blender等软件中建立好机构/骨骼模型&#xff0c;设计出脚踝和髋关节的运动曲线&#xff0c;然后进行逆运动学解算&#xff0c;测量每个关节在运动过程中的转…

重新访问了访客模式

访客模式是面向对象设计中最被高估但又被低估的模式之一。 高估了它&#xff0c;因为它常常被选择得太快&#xff08; 可能是由建筑宇航员选择的 &#xff09;&#xff0c;然后以错误的方式添加时会膨胀本来非常简单的设计。 如果您不遵循教科书示例&#xff0c;那么它可能会非…

ASP.NET—013:实现带控件的弹出层(弹出框)

http://blog.csdn.net/yysyangyangyangshan/article/details/38458169 在页面中用到弹出新页面的情况比较多的&#xff0c;一般来说都是使用JS方法showModalDialog("新页面相对路径?参数1&参数2",window,"新页面样式");然后会新弹出一个模态的page页。…

java 二进制 归属权限_【Java EE 学习 75 上】【数据采集系统第七天】【二进制运算实现权限管理】【权限分析和设计】...

一、权限计算相关分析1.如何存储权限首先说一下权限保存的问题&#xff0c;一个系统中最多有多少权限呢&#xff1f;一个大的系统中可能有成百上千个权限需要管理。怎么保存这么多的权限&#xff1f;首先&#xff0c;我们使用一个数字中的一位保存一种权限&#xff0c;那么如果…

GWT和HTML5 Canvas演示

这是我对GWT和HTML5 Canvas的第一个实验。 我的第一个尝试是创建矩形&#xff0c;仅用几行代码就得出了这样的内容&#xff1a; 码&#xff1a; public class GwtHtml5 implements EntryPoint {static final String canvasHolderId "canvasholder";static final St…

Solr管理界面详解

转载于:https://www.cnblogs.com/gslblog/p/6553813.html

一个实用的却被忽略的命名空间:Microsoft.VisualBasic:

当你看到这个命名空间的时候&#xff0c;别因为是vb的东西就匆忙关掉网页&#xff0c;那将会是您的损失&#xff0c;此命名空间中的资源最初目的是为了简化vb.net开发而创建的&#xff0c;所以microsoft.visualbasic并不属于system命名空间&#xff0c;而是独立存在的。虽然是为…

Linux基础之命令练习Day2-useradd(mod,del),groupadd(mod,del),chmod,chown,

作业一&#xff1a; 1) 新建用户natasha&#xff0c;uid为1000&#xff0c;gid为555&#xff0c;备注信息为“master” 2) 修改natasha用户的家目录为/Natasha 3) 查看用户信息配置文件的最后一行 4) 为natasha用户设置密码“123” 5) 查看用户密码配置文件的最后一行 6) 将nat…

动态表单,JSF世界早已等待

新的PrimeFaces扩展版本0.5.0带来了新的DynaForm组件。 通常&#xff0c;如果知道行/列的数量&#xff0c;元素的位置等&#xff0c;则可以通过h&#xff1a;panelGrid或p&#xff1a;panelGrid来构建非常简单的表单。 对于静态表单&#xff0c;这是正确的。 但是&#xff0c;如…

C# 定时器事件(设置时间间隔,间歇性执行某一函数,控制台程序)

定时器事件代码 static void Main(string[] args) {Method();#region 定时器事件 Timer aTimer new Timer();aTimer.Elapsed new ElapsedEventHandler(TimedEvent);aTimer.Interval seconds * 1000; //配置文件中配置的秒数aTimer.Enabled true;#endregionstring strLi…

Vmware安装Centos NAT方式设置静态IP

【Vmware中在搭建集群环境等&#xff0c;DHCP自动获取IP方式不方便&#xff0c;为了固定IP减少频繁更改配置信息&#xff0c;建议使用静态IP来配置&#xff0c;网络连接主要有三种方式 1.nat 2.桥接&#xff0c;3主机模式 &#xff0c;在这里主要介NAT方式&#xff0c; 为什么使…