ZetCode 其它教程(一)
原文:ZetCode
TCL 教程
这是 Tcl 教程。 在本教程中,您将学习 Tcl 语言。 本教程适合初学者。
目录
Tcl
Tcl 是基于字符串的脚本语言。 源代码被编译成字节码,然后由 Tcl 解释器解释。 它由 John Osterhout 于 1988 年创建。该语言通常用于快速原型制作,脚本应用,GUI 和测试。 Tcl 代表工具命令语言,其中 Tcl 脚本的源代码由命令组成。
相关教程
您可能也对 Tcl / Tk 教程或 Tkinter 教程感兴趣。
Tcl
在 Tcl 教程的这一部分中,我们将介绍 Tcl 编程语言。
目标
本教程的目标是使您开始使用 Tcl 编程语言。 本教程涵盖了 Tcl 语言的核心,即变量,列表,数组,控件结构和其他核心功能。 这不是该语言的完整介绍。 这是一种快速的入门数据。 该教程是在 Ubuntu Linux 上创建的。
Tcl
Tcl 是基于字符串的脚本语言。 源代码被编译成字节码,然后由 Tcl 解释器解释。 它是由 John Osterhout 在 1988 年创建的。其目的是创建一种易于嵌入到应用中的语言。 但通常在其原始区域之外使用。 该语言通常用于快速原型制作,脚本应用,GUI 和测试。 Tcl 代表工具命令语言,其中 Tcl 脚本的源代码由命令组成。
Tcl 是一种过程语言。 它具有一些函数式特征。 在 Tcl 版本 8.6 中添加了 OOP。
Tcl 及其 Tk GUI 工具包的官方网站是 tcl.tk
人气
今天有数百种编程语言在使用。 Tcl 不属于最受欢迎的 Tcl。 它使用的地方有它自己的利基。 例如,快速的原型制作,测试和数据库交互。
交互解释器
我们可以在脚本或交互式解释器中运行 Tcl 命令。 在本教程中,我们将使用交互式 Tcl 会话来演示一些较小的代码片段。 较大的代码示例将放在 Tcl 脚本中。
tclsh 是类似 shell 的应用,可从其标准输入或文件中读取 Tcl 命令并对其进行求值。 如果不带任何参数调用,它将以交互方式运行,从标准输入读取 Tcl 命令,并将命令结果和错误消息打印到标准输出。
$ tclsh
% puts $tcl_version
8.6
% puts $tcl_interactive
1
这是 Tcl 交互式会话的示例。
$ tclsh
我们使用tclsh
命令启动交互式会话。
% puts $tcl_version
8.6
提示将更改为%
字符。 我们将特殊的tcl_version
变量的值打印到控制台。 设置为当前使用的 Tcl 的版本。
% puts $tcl_interactive
1
tcl_interactive
变量告诉我们是否处于交互模式。
% exit
$
我们使用exit
命令终止交互式会话。 也可以使用 Ctrl + C
组合键。
Tcl 脚本
我们将有第一个简单的 Tcl 脚本示例。 Tcl 程序通常具有.tcl
扩展名。
#!/usr/bin/tclsh
# first.tcl
puts "This is Tcl tutorial"
在此脚本中,我们将消息打印到控制台。
#!/usr/bin/tclsh
UNIX 中的每个脚本都以 shebang 开头。 shebang 是脚本中的前两个字符:#!
。 shebang 之后是解释器的路径,它将执行我们的脚本。 /usr/bin/
是 Tcl 外壳最常见的位置。 它也可以位于/usr/local/bin/
或其他位置。
# first.tcl
Tcl 中的注释前面带有#
字符。
puts "This is Tcl tutorial"
puts
命令将字符串输出到控制台。
$ which tclsh
/usr/bin/tclsh
可以使用which
命令找到 Tcl 解释器的路径。
$ chmod +x first.tcl
$ ./first.tcl
This is Tcl tutorial
我们使用chmod
命令使脚本可执行并执行。
数据来源
以下资源用于创建本教程:
在 Tcl 教程的这一部分中,我们介绍了 Tcl 语言。
Tcl 语法结构
像人类语言一样,计算机语言也具有词汇结构。 Tcl 语言的词汇包括基本元素和适用于它们的规则。 单词是 Tcl 中的基本元素。 单词可以是命令,也可以是命令参数。 替换是 Tcl 语法的基本规则之一。
命令
Tcl 是基于字符串的解释命令语言。 Tcl 脚本由命令组成,这些命令由换行符或分号分隔。 命令是基本的执行元素。 命令后跟一个或多个单词(作为参数)。 每个参数都由空格分隔。
Tcl 命令的格式如下:command arg1 arg2 arg3 ...
Tcl 解释器接受句子的每个单词并对其求值。 第一个单词被认为是命令。 大多数 Tcl 命令是可变的。 这意味着它们可以处理可变数量的参数。
解析 Tcl 脚本后,将求值命令。 每个命令在其自己的上下文中解释单词。
puts "Tcl language"
在上面的代码摘录中,我们具有puts
命令。 此命令将消息打印到控制台。 "Tcl language"
是正在打印的字符串。 与其他语言不同,字符串不能用双引号引起来; 除非有空白。
#!/usr/bin/tclsh
puts zetcode.com; puts androida.co
puts zetcode
puts androida
在第一种情况下,命令用分号;
字符分隔。 在第二种情况下,它们之间用换行符分隔。
替换
Tcl 中存在三种替换。
- 命令替换
- 变量替换
- 反斜杠替换
方括号用于命令替换。
% puts [expr 1+2]
3
expr
命令用于执行算术计算。 首先,求值方括号之间的命令,并将结果返回到puts
命令。 然后,puts
命令求值结果并将其打印到控制台。
如果单词包含美元符号$
,则 Tcl 执行变量替换。 单词中的美元符号和以下字符由变量的值替换。
#!/usr/bin/tclsh
set name Jane
puts name
puts $name
我们创建一个名为name
的变量,并为其设置一个值。
puts name
在这种情况下,我们将字符串name
打印到控制台。
puts $name
在第二种情况下,参数以$
字符开头。 name
变量的值被打印到控制台。
$ ./name.tcl
name
Jane
这是示例的输出。
通过反斜杠替换,我们可以摆脱字符的原始含义。 例如,\n
代表换行,\t
是制表符。
% puts "This is \t Sparta"
This is Sparta
在这里,\t
序列被制表符代替。
#!/usr/bin/tclsh
puts "This was a \"great\" experience"
puts "The \\ character is the backslash character"
puts "20000\b\b miles"
如果要在引号字符中包含引号,请使用反斜杠替换。 另外,如果要打印\
字符,则必须在其前面加上其他反斜杠。 \b
替换为退格键。
$ ./backslash.tcl
This was a "great" experience
The \ character is the backslash character
200 miles
运行示例。
注释
人们使用注释来澄清源代码。 在 Tcl 中,注释以#
字符开头。
# example of a puts command
puts "Tcl language"
tclsh
会忽略#
字符之后的所有字符。
puts "Tcl language" ; # example of a puts command
仅当使用分号时才可以进行内联注释。
空格
空白用于分隔 Tcl 源中的单词。 它还用于提高源代码的可读性。
set name Jane
set
命令采用两个参数,两个参数之间用空格隔开。
set age 32
set name Robert
set occupation programmer
如果要提高源代码的清晰度,我们可能会使用更多空间。
set vals { 1 2 3 4 5 6 7 }
puts $vals
空白用于分隔 Tcl 列表中的项目。 在基于 C 的语言中,我们将使用逗号字符。
变量
变量是保存值的标识符。 在编程中,我们说我们为变量分配或设置了一个值。 从技术上讲,变量是对存储值的计算机内存的引用。 变量名称区分大小写。 这意味着Name
,name
和NAME
引用了三个不同的变量。
Tcl 中的变量是使用set
命令创建的。 要获取变量的值,其名称前面带有$
字符。
#!/usr/bin/tclsh
set name Jane
set Name Julia
set NAME Erika
puts $name
puts $Name
puts $NAME
在上面的脚本中,我们设置了三个变量。 变量名称相同,只是大小写不同。 但是,不建议这种做法。
$ ./case.tcl
Jane
Julia
Erika
这是case.tcl
脚本的输出。
大括号
大括号{}
在 Tcl 中具有特殊含义。 大括号内的单词替换已禁用。
#!/usr/bin/tclsh
set name {Julia Novak}
puts $name
puts "Her name is $name"
puts {Her name is $name}
这是一个小脚本,显示了 Tcl 中花括号的用法。
set name {Julia Novak}
可以使用大括号代替双引号来设置以空格分隔的字符串。
puts "Her name is $name"
在此变量被替换。
puts {Her name is $name}
使用花括号时,变量不会被替换。 一切都按字面意思打印。
$ ./braces.tcl
Julia Novak
Her name is Julia Novak
Her name is $name
braces.tcl
脚本的输出。
#!/usr/bin/tclsh
set numbers { 1 2 3 4 5 6 7 }
puts $numbers
puts "Braces {} are reserved characters in Tcl"
puts {Braces {} are reserved characters in Tcl}
大括号用于创建列表。 列表是 Tcl 中的基本数据类型。
set numbers { 1 2 3 4 5 6 7 }
在此处创建数字列表。
puts "Braces {} are reserved characters in Tcl"
puts {Braces {} are reserved characters in Tcl}
双引号或其他大括号内的大括号被视为常规字符,没有特殊含义。
$ ./braces2.tcl
1 2 3 4 5 6 7
Braces {} are reserved characters in Tcl
Braces {} are reserved characters in Tcl
braces2.tcl
脚本的输出。
方括号
方括号[]
用于创建嵌套命令。 这些嵌套命令在 Tcl 源代码行上的 main 命令之前执行。 它们用于将一个命令的结果作为参数传递给另一命令。
#!/usr/bin/tclsh
set cwd [pwd]
puts $cwd
puts [clock format [clock seconds] -format "%Y-%m-%d %T"]
在上面的代码示例中,我们显示了一些嵌套命令。
set cwd [pwd]
pwd
命令返回脚本的当前工作目录。 它放在方括号之间,使其成为嵌套命令。 首先执行pwd
命令,然后将命令的结果设置为cwd
变量。
puts [clock format [clock seconds] -format "%Y-%m-%d %T"]
嵌套命令可以嵌套在其他嵌套命令中。 首先,执行clock seconds
。 它以秒为单位返回当前本地时间。 结果传递到clock format
命令,该命令以可读形式格式化时间。 最后,格式化的时间返回到puts
命令,该命令将其打印到控制台。
$ ./nested.tcl
/home/janbodnar/prog/tcl/lexis
2015-01-16 16:45:04
示例的输出。
双引号
双引号将单词组合为命令的单个参数。 美元符号,方括号和反斜杠在引号内解释。
#!/usr/bin/tclsh
set distro Ubuntu
puts "The Linux distribution name is $distro"
puts "The current working directory: [pwd]"
puts "2000000\b\b\b miles"
这是在 Tcl 中使用引号的实际示例。
puts "The Linux distribution name is $distro"
变量发行版在引号字符内求值。 $distro
被替换为"Ubuntu"
。
puts "The current working directory: [pwd]"
方括号内的命令也会被解释。 在这里,我们使用pwd
命令获取当前的工作目录。
puts "2000000\b\b\b miles"
\b
转义序列删除前一个字符。 在我们的例子中,三个零被删除。
$ ./quotes.tcl
The Linux distribution name is Ubuntu
The current working directory: /home/janbodnar/prog/tcl/lexis
2000 miles
quotes.tcl
示例的输出。
反斜杠
反斜杠字符可以在 Tcl 中以三种不同的方式使用。 它引入了一些特殊字符,称为转义序列。 这些可以是换行符,选项卡,退格键等等。 它转义了特殊 Tcl 字符($, {}, "", \, ()
)的含义。 最后,它可以用作换行符。
#!/usr/bin/tclsh
puts "0\t1"
set name Jane
puts \$name
puts \\$name
puts "He said: \"There are plenty of them\""
puts "There are currently many Linux\
distributions in the world"
上面的脚本显示了如何在 Tcl 中使用反斜杠字符。
puts "0\t1"
\t
字符在 Tcl 中具有特殊含义。 它代表制表符空白字符。 当我们执行脚本时,在 0 和 1 内放置 8 个空格。
puts \$name
有了反斜杠,我们就可以摆脱美元符号的含义。 在我们的情况下,将$name
字符打印到控制台。
puts \\$name
反斜杠也可以转义。 这里,反斜杠字符和name
变量的值被打印到控制台。
puts "He said: \"There are plenty of them\""
我们可以通过转义内部双引号的含义来形成直接语音。
puts "There are currently many Linux\
distributions in the world"
如果源行太宽,我们可以使用反斜杠字符继续下一行,转义换行符。
$ ./backslash.tcl
0 1
$name
\Jane
He said: "There are plenty of them"
There are currently many Linux distributions in the world
运行示例。
圆括号
圆括号用于指示数组下标或更改expr
命令的运算符的优先级。
#!/usr/bin/tclsh
set names(1) Jane
set names(2) Robert
puts $names(1)
puts $names(2)
puts [expr (1+3)*5]
这是一个简单的示例,在 Tcl 中带有圆括号。
puts $names(1)
我们使用圆括号通过圆括号内指定的键访问值。
puts [expr (1+3)*5]
在这里,我们更改了运算符的优先级。 首先将 1 和 3 相加,然后将结果乘以 5。
$ ./roundb.tcl
Jane
Robert
20
这是round.tcl
脚本的输出。
在本章中,我们描述了 Tcl 语言的词汇。
Tcl 中的基本命令
在 Tcl 教程的这一部分中,我们介绍了一些基本的 Tcl 命令。
涵盖的 Tcl 命令包括puts
,open
,gets
,flush
,incr
,info
,set
和unset
命令。
puts
命令
在第一个示例中,我们将提到puts
命令。 puts
命令用于将消息打印到控制台或文件等其他通道。 该命令具有以下语法:
puts ?-nonewline? ?channelId? string
puts
是命令名称。 在问号之间指定了可选参数。
-nonewline
开关禁止换行符。 默认情况下,该命令在每条消息中放置换行符。 channelId
必须是开放通道(例如 Tcl 标准输入通道stdin
)的标识符,该标识符是调用open
或socket
的返回值。 如果未指定,则默认为标准输出通道stdout
。 最后,string
是要打印的消息。
#!/usr/bin/tclsh
puts "This is Tcl tutorial"
puts stdout "This is Tcl tutorial"
puts
命令将消息打印到控制台。 这两个命令调用执行相同的操作。
open
命令
open
命令打开文件,串行端口或命令管道,并返回通道标识符。 在下面的示例中,我们使用命令打开文件。
#!/usr/bin/tclsh
puts [open messages w] "This is Tcl tutorial"
puts
命令用于写入文件,该文件通过open
命令打开以进行写入。
$ cat messages
This is Tcl tutorial
我们将显示由上述 Tcl 脚本创建的消息文件的内容。
gets
和flush
命令
gets
命令从通道读取一条线,flush
命令刷新通道的缓冲输出。 在下面的示例中,我们创建一个向用户打招呼的脚本。
#!/usr/bin/tclsh
puts -nonewline "What is your name? "
flush stdout
gets stdin name
puts "Hello $name"
在此示例中,我们请求用户输入并以自定义问候语打印输入。
puts -nonewline "What is your name? "
-nonewline
选项禁止换行。 提示保持在同一行。
flush stdout
输出被缓冲。 要在命令运行后立即查看输出,我们可以使用flush
命令。 stdout
是标准输出。 在我们的例子中,它是一个终端。 它在 Tcl 中称为频道 ID。
gets stdin name
gets
命令从标准输入读取一行。 结果存储在名称变量中。
puts "Hello $name"
最后,我们向用户致意。
$ ./name.tcl
What is your name? Jan
Hello Jan
运行示例。
incr
命令
incr
递增变量的值。 它具有以下语法:
incr varName ?increment?
如果将参数传递给命令,则将其值添加到变量的值; 否则,该值将增加 1。
#!/usr/bin/tclsh
# incr_cmd.tcl
set x 5
incr x
puts $x
incr x 4
puts $x
该代码示例设置一个变量并将其递增两次。
set x 5
值 5 设置为x
变量。
incr x
puts $x
x
变量增加 1。数字 6 打印到控制台。
incr x 4
puts $x
数字 4 被添加到x
变量中。 puts
命令将 10 打印到控制台。
$ ./incr_cmd.tcl
6
10
这是incr_cmd.tcl
脚本的输出。
info
命令
info
命令返回有关 Tcl 解释器状态的信息。
#!/usr/bin/tclsh
puts [info tclversion]
puts [info host]
puts [info exists var]
info
命令具有多个选项。 我们展示其中的三个。
puts [info tclversion]
在这里,我们打印 Tcl 解释器的版本。
puts [info host]
该行显示主机名。
puts [info exists var]
最后,我们检查变量var
是否设置。
set
和unset
设置命令
set
命令用于创建和读取变量。 unset
命令销毁变量。
#!/usr/bin/tclsh
set x 23
puts $x
puts [set x]
unset x
puts [info exists x]
显示set
和unset
命令的示例。
set x 23
我们创建一个x
变量并为其分配值 23。
puts $x
我们打印x
变量的值。
puts [set x]
此行还打印x
变量的值。 具有一个参数的set
命令读取变量的值。 该值将传递到puts
命令并打印到终端。
unset x
变量x
被破坏。
puts [info exists x]
我们使用info exists
命令验证变量的存在。
命令行参数
像任何其他脚本一样,Tcl 脚本也可以使用命令行参数。 Tcl 具有三个预定义变量。
$argc
- 传递给脚本的参数数量$argv
- 参数列表$argv0
- 脚本名称
#!/usr/bin/tclsh
puts "The script has $argc arguments"
puts "The list of arguments: $argv"
puts "The name of the script is $argv0"
我们在此脚本中使用所有预定义的变量。
$ ./args.tcl 1 2 3
The script has 3 arguments
The list of arguments: 1 2 3
The name of the script is ./args.tcl
运行示例。
本章介绍了 Tcl 语言的一些基础知识。
Tcl 中的表达式
在 Tcl 教程的这一部分中,我们将讨论表达式。 在 Tcl 语言中,表达式未内置到核心语言中。 而是使用expr
命令对表达式求值。
表达式是根据操作数和运算符构造的。 表达式的运算符指示将哪些运算应用于操作数。 表达式中运算符的求值顺序由运算符的优先级和关联性确定。
运算符是特殊符号,表示已执行某个过程。 编程语言的运算符来自数学。 程序员处理数据。 运算符用于处理数据。 操作数是运算符的输入(参数)之一。
下表显示了 Tcl 语言中使用的一组运算符:
类别 | 符号 |
---|---|
按位,符号,逻辑非 | - + ~ ! |
求幂 | ** |
算术 | + - * / % |
移位 | << >> |
关系 | == != < > <= >= |
字符串比较 | eq ne |
列表 | in ni |
按位 | & | ^ |
布尔 | && || |
三元 | ?: |
一个运算符通常有一个或两个操作数。 那些仅使用一个操作数的运算符称为一元运算符。 那些使用两个操作数的对象称为二进制运算符。 还有一个三元运算符?:
,它可以处理三个操作数。
基本运算符
基本运算符是常用的运算符。 这些是符号运算符,算术运算符,模和幂运算符。
#!/usr/bin/tclsh
puts [expr +2]
puts [expr -2]
puts [expr -(-2)]
puts [expr 2+2]
puts [expr 2-2]
puts [expr 2*2]
puts [expr 2/2]
puts [expr 2/2.0]
puts [expr 2 % 2]
puts [expr 2 ** 2]
上面的示例显示了 Tcl 中常用运算符的用法。
puts [expr +2]
在此代码行中,我们使用加号运算符。 它对数量没有影响。 它仅表示该数字为正。 可以将其省略,并且在大多数情况下可以将其省略。
puts [expr -2]
puts [expr -(-2)]
负号运算符是强制性的。 它说数字是负数。 减号运算符更改数字的符号。 在第二行中,负号运算符将 -2 更改为正 2。
puts [expr 2+2]
puts [expr 2-2]
puts [expr 2*2]
puts [expr 2/2]
上面的几行显示了常用的算术运算符。
puts [expr 2 % 2]
% 是取模或余数的运算符。 它找到一个数除以另一个的余数。 表达式2 % 2
,2 模 2 为 0,因为 2 一次变成 2,余数为 0。因此代码行向控制台输出零。
puts [expr 2 ** 2]
这是求幂运算符。 代码行将 4 打印到控制台。
$ ./exp.tcl
2
-2
2
4
0
4
1
1.0
0
4
示例的输出。
除法运算符
刚入门的程序员通常会因除法运算而感到困惑。 在许多编程语言中,有两种除法运算:整数和非整数。 这也适用于 Tcl。
% expr 3/2
1
% expr 3/2.0
1.5
注意整数除法和浮点除法之间的区别。 当至少一个操作数是浮点数时,结果也就是浮点值。 结果更加准确。 如果两个操作数均为整数,则结果也为整数。
赋值和增量运算符
Tcl 中没有赋值运算符=
,也没有增量和减量(++
和--
)运算符。 这些运算符在其他计算机语言中很常见。 取而代之的是,Tcl 具有命令。
% set a 5
5
% incr a
6
% incr a
7
% incr a -1
6
上面的代码显示了哪些命令用于实现缺少的运算符。
% set a 5
在 Python 中,我们将执行a = 5
。 在 Tcl 中,我们使用set
命令将值设置为变量。
% incr a
6
在 C,Java 和许多其他语言中,我们将通过以下方式将变量增加一个:a++;
。 在 Tcl 中,我们使用incr
命令。 默认情况下,该值增加 1。
% incr a -1
6
上面的代码显示了如何将变量减 1,这由--
减量运算符以基于 C 的语言完成。
布尔运算符
在 Tcl 中,我们具有以下逻辑运算符:
符号 | 名称 |
---|---|
&& |
逻辑与 |
|| |
逻辑或 |
! |
否定 |
布尔运算符也称为逻辑运算符。
#!/usr/bin/tclsh
set x 3
set y 8
puts [expr $x == $y]
puts [expr $y > $x]
if {$y > $x} {
puts "y is greater than x"
}
许多表达式导致布尔值。 布尔值用于条件语句中。
puts [expr $x == $y]
puts [expr $y > $x]
关系运算符始终导致布尔值。 这两行显示 0 和 1。在 Tcl 中,0 为false
,任何非零值为true
。
if {$y > $x} {
puts "y is greater than x"
}
仅当满足括号内的条件时,才执行if
命令的主体。 $y > $x
返回 true,因此消息"y
大于x"
被打印到终端。
#!/usr/bin/tclsh
puts [expr 0 && 0]
puts [expr 0 && 1]
puts [expr 1 && 0]
puts [expr 1 && 1]
此示例显示了逻辑和&&
运算符。 仅当两个操作数均为true
时,它的求值结果为true
。
$ ./andoperator.tcl
0
0
0
1
如果两个操作数中的任何一个为true
,则逻辑或||
运算符的计算结果为true
。
#!/usr/bin/tclsh
puts [expr 0 || 0]
puts [expr 0 || 1]
puts [expr 1 || 0]
puts [expr 1 || 1]
如果运算符的任一侧为真,则操作的结果为真。
$ ./oroperator.tcl
0
1
1
1
否定运算符!
将true
设为false
,并将false
设为false
。
#!/usr/bin/tclsh
puts [expr ! 0]
puts [expr ! 1]
puts [expr ! (4<3)]
该示例显示了否定运算符的作用。
$ ./not.tcl
1
0
1
||
和&&
运算符经过短路求值。 短路求值意味着仅当第一个参数不足以确定表达式的值时才求值第二个参数:当逻辑的第一个参数的值等于false
时,总值必须为false
; 当逻辑或的第一个参数为true
时,总值必须为true
。 短路求值主要用于提高性能。
一个例子可以使这一点更加清楚。
#!/usr/bin/tclsh
proc One {} {
puts "Inside one"
return false
}
proc Two {} {
puts "Inside two"
return true
}
puts "Short circuit"
if { [One] && [Two] } {
puts "Pass"
}
puts "###################"
if { [Two] || [One] } {
puts "Pass"
}
在示例中,我们有两个过程。 (过程和条件将在后面描述。)它们在布尔表达式中用作操作数。 我们将看看它们是否被调用。
if { [One] && [Two] } {
puts "Pass"
}
One
过程返回false
。 短路 &&
不求值第二步。 没有必要。 一旦操作数为假,逻辑结论的结果始终为假。 控制台上仅打印"Inside one"
。
puts "###################"
if { [Two] || [One] } {
puts "Pass"
}
在第二种情况下,我们使用||
运算符,并使用Two
过程作为第一个操作数。 在这种情况下,"Inside two"
和"Pass"
字符串将打印到终端。 同样,也不必求值第二个操作数,因为一旦第一个操作数计算为true
,则逻辑或始终为true
。
$ ./shortcircuit.tcl
Short circuit
Inside one
###################
Inside two
Pass
shorcircuit.tcl
脚本的结果。
关系运算符
关系运算符用于比较值。 这些运算符总是产生布尔值。 在 Tcl 中,0 代表false
,1 代表true
。 关系运算符也称为比较运算符。
符号 | 含义 |
---|---|
< |
小于 |
<= |
小于或等于 |
> |
大于 |
>= |
大于或等于 |
== |
等于 |
!= |
不等于 |
该表显示了六个 Tcl 关系表达式。
#!/usr/bin/tclsh
puts [expr 3 < 4]
puts [expr 3 == 4]
puts [expr 4 >= 3]
puts [expr 4 != 3]
在 Tcl 中,我们使用==
运算符比较数字。 某些语言(例如 Ada,Visual Basic 或 Pascal)使用=
比较数字。
$ ./rel.tcl
1
0
1
1
该示例打印四个布尔值。
按位运算符
小数对人类是自然的。 二进制数是计算机固有的。 二进制,八进制,十进制和十六进制符号仅是相同数字的符号。 按位运算符使用二进制数的位。 像 Tcl 这样的高级语言很少使用按位运算符。
符号 | 含义 |
---|---|
~ |
按位取反 |
^ |
按位异或 |
& |
按位与 |
| |
按位或 |
按位取反运算符分别将 1 更改为 0,将 0 更改为 1。
% puts [expr ~7]
-8
% puts [expr ~-8]
7
运算符将 7 的所有位都还原。这些位之一还确定该数字是否为负。 如果我们再一次对所有位取反,我们将再次得到 7。
按位,运算符在两个数字之间进行逐位比较。 仅当操作数中的两个对应位均为 1 时,位位置的结果才为 1。
00110
& 00011
= 00010
第一个数字是二进制表示法 6,第二个数字是 3,结果是 2。
% puts [expr 6 & 3]
2
% puts [expr 3 & 6]
2
按位或运算符在两个数字之间进行逐位比较。 如果操作数中的任何对应位为 1,则位位置的结果为 1。
00110
| 00011
= 00111
结果为00110
或十进制 7。
% puts [expr 6 | 3]
7
% puts [expr 3 | 6]
7
按位互斥或运算符在两个数字之间进行逐位比较。 如果操作数中对应位中的一个或另一个(但不是全部)为 1,则位位置的结果为 1。
00110
^ 00011
= 00101
结果为00101
或十进制 5。
% puts [expr 6 ^ 3]
5
% puts [expr 3 ^ 6]
5
扩展运算符
扩展运算符{*}
使列表中的每个项目成为当前命令的单独参数。 列表是基本的 Tcl 数据结构; 在下一章中将进行介绍。
#!/usr/bin/tclsh
set nums {1 2 3 4 5 6}
puts $nums
puts [tcl::mathfunc::max {*}$nums]
puts [tcl::mathfunc::min {*}$nums]
扩展运算符与两个数学函数一起使用。
set nums {1 2 3 4 5 6}
将创建一个名为nums
的数字列表。 列表是值的有序集合。
puts $nums
列表的内容被打印到终端。
puts [tcl::mathfunc::max {*}$nums]
tcl::mathfunc::max
是标准的数学函数。 它不处理列表。 数字应作为单独的参数传递。 扩展运算符将项目列表转换为单个项目。
$ ./expansion.tcl
1 2 3 4 5 6
6
1
示例输出。
运算符优先级
运算符优先级告诉我们首先求值哪个运算符。 优先级对于避免表达式中的歧义是必要的。
以下表达式 28 或 40 的结果是什么?
3 + 5 * 5
像数学中一样,乘法运算符的优先级高于加法运算符。 结果是 28。
(3 + 5) * 5
我们使用括号来更改求值顺序。 括号内的表达式始终首先被求值。
下表显示了按优先级排序的常见 Tcl 运算符(最高优先级优先):
类别 | 符号 | 关联性 |
---|---|---|
按位,逻辑非 | - + ~ ! |
左 |
求幂 | ** |
左 |
算术 | + - * / % |
左 |
移位 | << >> |
左 |
关系 | == != < > <= >= |
左 |
字符串比较 | eq ne |
左 |
列表 | in ni |
左 |
按位 | & | ^ |
左 |
布尔 | && || |
左 |
三元 | ?: |
右 |
表的同一行上的运算符具有相同的优先级。
!/usr/bin/tclsh
puts [expr 3 + 5 * 5]
puts [expr (3 + 5) * 5]
puts [expr ! 1 || 1]
puts [expr ! (1 || 1)]
在此代码示例中,我们显示一些常见的表达式。 每个表达式的结果取决于优先级。
puts [expr 3 + 5 * 5]
该行打印 28。乘法运算符的优先级高于加法。 首先,计算5*5
的乘积,然后加 3。
puts [expr (3 + 5) * 5]
圆括号可用于更改优先级。 在上面的表达式中,将 3 加到 5,然后将结果乘以 5。
puts [expr ! 1 || 1]
在这种情况下,否定运算符具有更高的优先级。 首先,第一个true
(1)值取反为false
(0),然后||
运算符将false
和true
组合在一起,最后得到true
。
$ ./precedence.tcl
28
40
1
0
输出。
关联性
有时,优先级不能令人满意地确定表达式的结果。 还有另一个规则称为关联性。 运算符的关联性确定优先级与相同的运算符的求值顺序。
9 / 3 * 3
此表达式的结果是 9 还是 1? 乘法,删除和模运算符从左到右关联。 因此,该表达式的计算方式为:(9 / 3) * 3
,结果为 9。
算术,布尔,关系和按位运算符都是从左到右关联的。
三元运算符是正确关联的。
三元运算符
三元运算符?:
是条件运算符。 对于要根据条件表达式选择两个值之一的情况,它是一个方便的运算符。
cond-exp ? exp1 : exp2
如果cond-exp
为true
,则求值exp1
并返回结果。 如果cond-exp
为false
,则求值exp2
并返回其结果。
#!/usr/bin/tclsh
set age 32
set adult [expr $age >= 18 ? true : false]
puts "Adult: $adult"
在大多数国家/地区,成年取决于您的年龄。 如果您的年龄超过特定年龄,则您已经成年。 对于三元运算符,这是一种情况。
set adult [expr $age >= 18 ? true : false]
首先,求值赋值运算符右侧的表达式。 三元运算符的第一阶段是条件表达式求值。 因此,如果年龄大于或等于 18,则返回?
字符后的值。 如果不是,则返回:
字符后的值。 然后将返回值分配给成人变量。
$ ./ternary.tcl
Adult: true
32 岁的成年人是成人。
计算素数
我们将计算素数。 本教程的稍后部分将介绍一些功能(列表,循环)。
#!/usr/bin/tclsh
# primes.tcl
set nums { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
}
puts "Prime numbers:"
foreach num $nums {
if { $num==1 } { continue }
if { $num==2 || $num==3 } {
puts -nonewline "$num "
continue
}
set i [expr int(sqrt($num))]
set isPrime true
while { $i > 1 } {
if { $num % $i == 0 } {
set isPrime false
}
incr i -1
}
if { $isPrime } {
puts -nonewline "$num "
}
}
puts ""
在上面的示例中,我们处理了许多不同的运算符。 质数(或质数)是大于 1 的自然数,具有正好两个不同的自然数除数:1 和它本身。 我们拾取一个数字并将其除以数字,从 1 到拾取的数字。 实际上,我们不必尝试所有较小的数字,我们可以将数字除以所选数字的平方根。 该公式将起作用。 我们使用余数除法运算符。
set nums { 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
}
我们从该整数列表中计算素数。
if { $num==1 } { continue }
根据定义,1 不是质数。 continue
命令跳到循环的下一个迭代。
if { $num==2 || $num==3 } {
puts -nonewline "$num "
continue
}
我们跳过 2 和 3 的计算。它们是质数,不需要进一步的计算。 请注意等式和条件或运算符的用法。 ==
的优先级高于||
运算符。 因此,我们不需要使用括号。
set i [expr int(sqrt($num))]
如果我们仅尝试小于所讨论数字的平方根的数字,那么我们可以。
while { $i > 1 } {
if { $num % $i == 0 } {
set isPrime false
}
incr i -1
}
在此while
循环中,i
是计算出的数字的平方根。 我们使用incr
命令将每个循环周期将i
减少 1。 当i
小于 1 时,循环结束。 例如,我们有 9。9 的平方根是 3。我们将 9 的数字除以 3 和 2。这对于我们的计算就足够了。
if { $isPrime } {
puts -nonewline "$num "
}
如果余数除法运算符针对任何 i 值返回 0,则说明该数字不是质数。
$ ./primes.tcl
Prime numbers:
2 3 5 7 11 13 17 19 23 29 31
这是primes.tcl
脚本的输出。
在 Tcl 教程的这一部分中,我们介绍了 Tcl 表达式。
Tcl 中的控制流
在 Tcl 教程的这一部分中,我们将讨论流控制。 我们将定义几个命令,这些命令使我们能够控制 Tcl 脚本的流程。
在 Tcl 语言中,有几个命令可用于更改程序流程。 运行程序时,其命令从源文件的顶部到底部执行。 逐一。 可以通过特定命令更改此流程。 命令可以多次执行。 有些命令是有条件的。 仅在满足特定条件时才执行它们。
if
命令
if
命令具有以下一般形式:
if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN?
if
命令用于检查表达式是否为真。 如果为真,则执行命令主体。 主体用花括号括起来。
if
命令计算一个表达式。 该表达式必须返回一个布尔值。 在 Tcl 中,1,是,true
表示true
,0,no
,false
表示false
。
!/usr/bin/tclsh
if yes {
puts "This message is always shown"
}
在上面的示例中,始终执行由{ }
字符包围的主体。
#!/usr/bin/tclsh
if true then {
puts "This message is always shown"
}
then
命令是可选的。 如果我们认为可以使用它,它将使代码更加清晰。
我们可以使用else
命令创建一个简单的分支。 如果if
命令后方括号内的表达式的计算结果为false
,则将自动执行else
命令后方的命令。
#!/usr/bin/tclsh
set sex female
if {$sex == "male"} {
puts "It is a boy"
} else {
puts "It is a girl"
}
我们有性别变量。 它具有"female"
字符串。 布尔表达式的计算结果为false
,我们在控制台中得到"It is a girl"
。
$ ./girlboy.tcl
It is a girl
我们可以使用elseif
命令创建多个分支。 仅当不满足先前条件时,elseif
命令才会测试其他条件。 请注意,我们可以在测试中使用多个elseif
命令。
#!/usr/bin/tclsh
# nums.tcl
puts -nonewline "Enter a number: "
flush stdout
set a [gets stdin]
if {$a < 0} {
puts "the number is negative"
} elseif { $a == 0 } {
puts "the numer is zero"
} else {
puts "the number is positive"
}
在上面的脚本中,我们提示您输入一个值。 我们测试该值是否为负数或正数或等于零。 如果第一个表达式的计算结果为false
,则对第二个表达式进行计算。 如果不满足先前的条件,则将执行else
命令之后的主体。
$ ./nums.tcl
Enter a number: 2
the number is positive
$ ./nums.tcl
Enter a number: 0
the numer is zero
$ ./nums.tcl
Enter a number: -3
the number is negative
多次运行该示例。
switch
命令
switch
命令将其字符串参数与每个模式参数按顺序进行匹配。 一旦找到与字符串匹配的模式,它就会通过将其递归传递给 Tcl 解释器来求值以下主体参数,并返回该求值结果。 如果最后一个模式参数为默认值,则它匹配任何内容。 如果没有任何模式参数与字符串匹配,并且没有给出默认值,那么switch
命令将返回一个空字符串。
#!/usr/bin/tclsh
# switch_cmd.tcl
puts -nonewline "Select a top level domain name:"
flush stdout
gets stdin domain
switch $domain {
us { puts "United States" }
de { puts Germany }
sk { puts Slovakia }
hu { puts Hungary }
default { puts "unknown" }
}
在脚本中,我们提示您输入域名。 有几种选择。 例如,如果该值等于我们,则将"United States"
字符串打印到控制台。 如果该值与任何给定值都不匹配,那么将执行默认主体,并将unknown
打印到控制台。
$ ./switch_cmd.tcl
Select a top level domain name:sk
Slovakia
我们已将sk
字符串输入到控制台,该程序响应了斯洛伐克。
while
命令
while
命令是一个控制流命令,它允许根据给定的布尔条件重复执行代码。
while
命令在大括号括起来的块内执行命令。 每次将表达式求值为true
时都将执行命令。
#!/usr/bin/tclsh
# whileloop.tcl
set i 0
set sum 0
while { $i < 10 } {
incr i
incr sum $i
}
puts $sum
在代码示例中,我们从一系列数字计算值的总和。
while
循环包含三个部分:初始化,测试和更新。 该命令的每次执行都称为一个循环。
set i 0
我们启动i
变量。 它用作计数器。
while { $i < 10 } {
...
}
while
命令后面大括号内的表达式是第二阶段,即测试。 执行主体中的命令,直到表达式的计算结果为false
。
incr i
while
循环的最后第三阶段是更新。 计数器增加。 请注意,对while
循环的不正确处理可能会导致循环不断。
for
命令
如果在启动循环之前知道周期数,则可以使用for
命令。 在此构造中,我们声明一个计数器变量,该变量在每次循环重复期间都会自动增加或减少值。
#!/usr/bin/tclsh
for {set i 0} {$i < 10} {incr i} {
puts $i
}
在此示例中,我们将数字0..9
打印到控制台。
for {set i 0} {$i < 10} {incr i} {
puts $i
}
分为三个阶段。 首先,我们将计数器i
初始化为零。 此阶段仅完成一次。 接下来是条件。 如果满足条件,则执行for
块中的命令。 然后进入第三阶段; 计数器增加。 现在,我们重复阶段 2 和 3,直到不满足条件并留下for
循环。 在我们的情况下,当计数器i
等于 10 时,for
循环停止执行。
$ ./forloop.tcl
0
1
2
3
4
5
6
7
8
9
在这里,我们看到forloop.tcl
脚本的输出。
foreach
命令
foreach
命令简化了遍历数据集合的过程。 它没有明确的计数器。 它逐个元素地遍历一个列表,并且当前值被复制到构造中定义的变量中。
#!/usr/bin/tclsh
set planets { Mercury Venus Earth Mars Jupiter Saturn
Uranus Neptune }
foreach planet $planets {
puts $planet
}
在此示例中,我们使用foreach
命令浏览行星列表。
foreach planet $planets {
puts $planet
}
foreach
命令的用法很简单。 planets
是我们迭代的列表。 planet
是具有列表中当前值的临时变量。 foreach
命令遍历所有行星并将其打印到控制台。
$ ./planets.tcl
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
运行上面的 Tcl 脚本将给出此输出。
#!/usr/bin/tclsh
set actresses { Rachel Weiss Scarlett Johansson Jessica Alba \
Marion Cotillard Jennifer Connelly}
foreach {first second} $actresses {
puts "$first $second"
}
在此脚本中,我们迭代列表的值对。
foreach {first second} $actresses {
puts "$first $second"
}
我们在每次迭代时从列表中选择两个值。
$ ./actresses.tcl
Rachel Weiss
Scarlett Johansson
Jessica Alba
Marion Cotillard
Jennifer Connelly
这是actresses.tcl
脚本的输出。
#!/usr/bin/tclsh
foreach i { one two three } item {car coins rocks} {
puts "$i $item"
}
我们可以并行遍历两个列表。
$ ./parallel.tcl
one car
two coins
three rocks
这是parallel.tcl
脚本的输出。
break
和continue
命令
break
命令可用于终止由while
,for
或switch
命令定义的块。
#!/usr/bin/tclsh
while true {
set r [expr 1 + round(rand()*30)]
puts -nonewline "$r "
if {$r == 22} { break }
}
puts ""
我们定义了一个无限的while
循环。 我们使用break
命令退出此循环。 我们从 1 到 30 中选择一个随机值并打印出来。 如果该值等于 22,则结束无穷while
循环。
set r [expr 1 + round(rand()*30)]
在这里,我们计算出 1..30 之间的随机数。 rand()
是内置的 Tcl 过程。 它返回一个从 0 到 0.99999 的随机数。 rand()*30
返回 0 到 29.99999 之间的随机数。 round()
过程将最终数字四舍五入。
$ ./breakcommand.tcl
28 20 8 8 12 22
我们可能会得到这样的东西。
continue
命令用于跳过循环的一部分,并继续循环的下一个迭代。 可以与for
和while
命令结合使用。
在下面的示例中,我们将打印一个数字列表,这些数字不能除以 2 而没有余数。
#!/usr/bin/tclsh
set num 0
while { $num < 100 } {
incr num
if {$num % 2 == 0} { continue }
puts "$num "
}
puts ""
我们使用while
循环遍历数字1..99
。
if {$num % 2 == 0} { continue }
如果表达式num % 2
返回 0,则所讨论的数字可以除以 2。执行continue
命令,并跳过循环的其余部分。 在我们的情况下,循环的最后一个命令将被跳过,并且数字不会打印到控制台。 下一个迭代开始。
在 Tcl 教程的这一部分中,我们正在讨论控制流结构。
Tcl 中的字符串
在 Tcl 教程的这一部分中,我们将更详细地处理字符串数据。 字符串是计算机语言中的一种重要数据类型。
字符串是字符序列。 与其他语言不同,Tcl 中的字符串不必总是用双引号引起来。 仅当单词之间有空格时,它们才是必需的。 Tcl 是基于字符串的语言。 它提供了一组丰富的用于处理字符串的命令。
第一个例子
下面是一个显示一些字符串的简单示例。
#!/usr/bin/tclsh
puts Tcl
puts Java
puts Falcon
puts "Tcl language"
puts {Tcl language}
该脚本将一些字符串值打印到控制台。
puts Tcl
puts Java
puts Falcon
Tcl 中的字符串不必总是用引号引起来。
puts "Tcl language"
puts {Tcl language}
Tcl 中的字符串可以用双引号或大括号括起来。
$ ./simple_strings.tcl
Tcl
Java
Falcon
Tcl language
Tcl language
这是simple.tcl
脚本的输出。
使用引号
如果我们想在直接演讲中显示报价该怎么办? 在这种情况下,必须对内引号进行转义。
$ cat directspeech.tcl
#!/usr/bin/tclsh
puts "There are many stars"
puts "He said, \"Which one is your favourite?\""
我们使用\
字符转义其他引号。
$ ./directspeech.tcl
There are many stars
He said, "Which one is your favourite?"
directspeech.tcl
程序的输出。
多行字符串
在 Tcl 中创建多行字符串非常容易。 在创建多行字符串的许多其他语言中,我使用起来不那么方便。
#!/usr/bin/tclsh
set lyrics "I cheated myself
like I knew I would
I told ya, I was trouble
you know that I'm no good"
puts $lyrics
我们简单地继续下一行。 如果我们想显示经文,这很有用。
$ ./multiline.tcl
I cheated myself
like I knew I would
I told ya, I was trouble
you know that I'm no good
比较字符串
字符串的基本比较可以通过string compare
命令完成。
#!/usr/bin/tclsh
puts [string compare 12 12]
puts [string compare Eagle Eagle]
puts [string compare Eagle eagle]
puts [string compare -nocase Eagle eagle]
string compare
命令逐字符比较字符串。 如果发现两个字符串的第一个字符相等,则继续第二个字符,直到结尾。 如果字符串相等,则返回 0;如果第一个字符串中的不匹配字符位于 ASCII 表中的第二个字符串之前,则返回 -1。 如果第一个字符串的不匹配字符位于第二个字符串的字符之后,则返回 1。
puts [string compare 12 12]
在这种情况下,12 是字符串。
puts [string compare Eagle Eagle]
两个字符串相等,将 0 打印到控制台。
puts [string compare Eagle eagle]
E
位于e
之前,因此返回-1。
puts [string compare -nocase Eagle eagle]
使用-nocase
选项,我们将忽略大小写。 这两个字符串相等。
$ ./compare.tcl
0
0
-1
0
程序的输出。
string equal
也可以用来比较字符串。 如果字符串相等,则命令返回 1,否则返回 0。
#!/usr/bin/tclsh
set str1 Tcl
set str2 "Tcl language"
puts [string compare $str1 $str2]
puts [string compare -length 3 $str1 $str2]
puts [string equal $str1 $str2]
puts [string equal -length 3 $str1 $str2]
该脚本显示了两个比较字符串的命令。
puts [string compare $str1 $str2]
该行打印 -1。 前三个位置的字符相等。 在第四个位置,string compare
命令将空格与l
字符进行比较。 该空格位于 ASCII 表中的l
字符之前。 字符串不相等。
puts [string compare -length 3 $str1 $str2]
在这种情况下,我们将比较限制为前三个字符。 它们在两个字符串中都相同,因此命令返回 0。
puts [string equal $str1 $str2]
这两个字符串不相同,因此string equal
命令返回 0,表示为false
。
puts [string equal -length 3 $str1 $str2]
将字符串限制为前三个字符,该命令返回 1。这意味着直到前三个字符为止它们都是相同的。
模式匹配
对于简单的模式匹配-球形-我们可以使用string match
命令。 对于更强大的模式匹配,我们可以利用regexp
命令。
#!/usr/bin/tclsh
puts [string match book???? bookcase]
puts [regexp {[a-z]{3}} "abc"]
puts [regexp {[^a-z]{3}} "abc"]
puts [regexp book(shelf|worm) bookworm]
该示例演示了string match
和regexp
命令的用法。 对于匹配项,它们返回 1,对于不匹配项,返回 0。
$ ./string_match.tcl
1
1
0
1
string_match1.tcl
程序的输出。
Unicode
我们可以在 Tcl 脚本中使用 Unicode 字符串。
#!/usr/bin/tclsh
puts "La femme vit par le sentiment, là où l'homme vit par l'action"
puts "Анна Каренина"
我们将两个消息打印到终端。 第一是法语,第二是俄语。
$ ./unicode.tcl
La femme vit par le sentiment, là où l'homme vit par l'action
Анна Каренина
输出。
字符串命令
Tcl 具有有用的内置命令,可用于处理字符串。
#!/usr/bin/tclsh
set str Eagle
puts [string length $str]
puts [string index $str 0]
puts [string index $str end]
puts [string range $str 1 3]
我们定义一个字符串变量并使用一些字符串命令。
puts [string length $str]
string length
返回字符串中的字符数。
puts [string index $str 0]
puts [string index $str end]
string index
命令在特定位置返回字符。
puts [string range $str 1 3]
string range
返回由第一个和最后一个索引选择的字符范围。
$ ./strings1.tcl
5
E
e
agl
输出。
我们有一个split
命令将字符串拆分为特定字符。 该命令返回单词列表。 可以使用join
命令将这些单词组合成一个字符串。
#!/usr/bin/tclsh
set langs "Tcl,Java,C,C#,Ruby,Falcon"
puts [split $langs ,]
puts [join [split $langs ","] ":"]
在我们的程序中,我们将拆分并连接字符串。
set langs "Tcl,Java,C,C#,Ruby,Falcon"
这是我们将要拆分的字符串。 有几个单词,以逗号分隔。 逗号字符是用来分割字符串的字符。
puts [split $langs ,]
该行显示了我们从字符串中拆分出的所有单词。
puts [join [split $langs ","] ":"]
split
命令返回字符串中的单词列表。 然后将这些词合并在一起。 单词现在将由冒号分隔。
$ ./splitjoin.tcl
Tcl Java C C# Ruby Falcon
Tcl:Java:C:C#:Ruby:Falcon
示例的输出。
接下来,我们将使用另一个带有几个字符串命令的示例。
#!/usr/bin/tclsh
set str "ZetCode"
puts [string toupper $str]
puts [string tolower $str]
puts [string totitle $str]
puts [string reverse $str]
我们介绍了四个字符串命令。 这些命令不会更改原始字符串。 它们返回一个新的,经过修改的字符串。
puts [string toupper $str]
我们将字符转换为大写。
puts [string tolower $str]
我们将字符串的字母转换为小写。
puts [string totitle $str]
string totitle
返回一个字符串,其首字母大写; 其他字符均为小写。
puts [string reverse $str]
我们使用string reverse
命令反转字符串的字符。
$ ./strings2.tcl
ZETCODE
zetcode
Zetcode
edoCteZ
运行程序。
格式化字符串
字符串的最基本格式在引号内完成。
#!/usr/bin/tclsh
set oranges 2
set apples 4
set bananas 3
puts "There are $oranges oranges, $apples apples and\
$bananas bananas. "
Tcl 用双引号求值变量。
puts "There are $oranges oranges, $apples apples and\
$bananas bananas. "
在此代码行中,我们将变量和字符串组合在一个句子中。
$ ./fruit.tcl
There are 2 oranges, 4 apples, and 3 bananas.
输出。
可以使用format
命令完成更高级的格式化。 它具有以下概要:
format formatString ?arg arg ...?
formatString
用于控制参数的显示方式。 该命令可以使用多个参数。
#!/usr/bin/tclsh
puts [format %s "Inception movie"]
puts [format "%d %s" 23 songs]
这是基本脚本,显示format
命令的用法。
puts [format %s "Inception movie"]
该行仅将字符串输出到控制台。
puts [format "%d %s" 23 songs]
在这里,我们打印两个参数。 每个参数都有一个格式说明符,该说明符以%
字符开头。
$ ./basicformat.tcl
Inception movie
23 songs
输出。
现在,我们显示format
命令的一些基本转换说明符。 %s
,%f
,%d
和%e
是转换类型。 它们控制如何显示值。 转换类型是转换说明符的唯一必需部分。
#!/usr/bin/tclsh
puts [format "%s" "Tcl language"]
puts [format "%f" 212.432]
puts [format "%d" 20000]
puts [format "%e" 212.342]
我们将在终端上打印四则消息。
puts [format "%s" "Tcl language"]
%s
是字符串的转换类型。
puts [format "%f" 212.432]
%f
用于显示十进制数字。
puts [format "%d" 20000]
要打印整数值,我们使用%d
转换类型。
puts [format "%e" 212.342]
%e
用于以科学格式显示数字。
$ ./format.tcl
Tcl language
212.432000
20000
2.123420e+02
输出。
在下一个示例中,我们将以三种不同的数字格式设置数字格式。
#!/usr/bin/tclsh
puts [format "%-10s %-14s %s" Decimal Hexadecimal Octal]
puts [format "%-10d %-14x %o" 5000 5000 5000]
puts [format "%-10d %-14x %o" 344 344 344]
puts [format "%-10d %-14x %o" 55 55 55]
puts [format "%-10d %-14x %o" 9 9 9]
puts [format "%-10d %-14x %o" 15666 15666 15666]
我们以十进制,十六进制和八进制格式打印数字。 我们还将数字对齐三列。
puts [format "%-10d %-14x %o" 5000 5000 5000]
%-10d
适用于第一个数字,%-14x
适用于第二个数字,%o
适用于第三个数字。 我们将描述第一个。 格式说明符以%
字符开头。 负号-
表示如果该值短于字段宽度,则将其对齐。 其余字段填充空白。 数字 10 指定字段宽度。 最后,d
字符表明数字以十进制格式显示。 x
代表十六进制,o
代表八进制。
$ ./numbers.tcl
Decimal Hexadecimal Octal
5000 1388 11610
344 158 530
55 37 67
9 9 11
15666 3d32 36462
运行示例。
最后,我们将格式化日期和时间数据。 我们使用clock format
命令。
#!/usr/bin/tclsh
set secs [clock seconds]
puts "Short date: [clock format $secs -format %D]"
puts "Long date: [clock format $secs -format "%A, %B %d, %Y"]"
puts "Short time: [clock format $secs -format %R]"
puts "Long time: [clock format $secs -format %r]"
puts "Month: [clock format $secs -format %B]"
puts "Year: [clock format $secs -format %Y]"
前面的示例演示了一些常见的日期和时间格式。
set secs [clock seconds]
我们以秒为单位获取当前时间戳。 此值随后传递给clock format
命令,以使人类可以读取日期和时间。
puts "Short date: [clock format $secs -format %D]"
日期格式由-format
选项控制。 有几种说明符。 %D
以月/日/年格式返回日期。
$ ./clockformat.tcl
Short date: 04/11/2011
Long date: Monday, April 11, 2011
Short time: 11:30
Long time: 11:30:30 am
Month: April
Year: 2011
输出。
Tcl 教程的这一部分介绍了字符串。
{% raw %}
Tcl 列表
在 Tcl 教程的这一部分中,我们将讨论列表。
计算机程序可以处理数据。 处理数据组是一项基本的编程操作。 在 Tcl 中,列表是基本数据结构。 它是有价物品的有序集合。 列表中的项目用空格隔开。
列表中的每个项目均由其索引标识。 列表没有固定的长度。 列表元素可以是字符串,数字,变量,文件或其他列表。 我们可以将列表嵌套到其他任何深度的列表中。
创建列表
我们可以通过多种方式在 Tcl 中创建列表。
#!/usr/bin/tclsh
set l1 { 1 2 3 }
set l2 [list one two three]
set l3 [split "1.2.3.4" .]
puts $l1
puts $l2
puts $l3
创建了三个列表,并将它们的元素打印到控制台。
set l1 { 1 2 3 }
创建列表的基本方法是将列表的元素放在大括号内。 列表元素由空格分隔。
set l2 [list one two three]
创建列表的另一种方法是使用list
命令。
set l3 [split "1.2.3.4" .]
一些 Tcl 命令作为结果返回一个列表。 在上面的代码行中,split
命令返回从字符串生成的数字列表。
$ ./createlists.tcl
1 2 3
one two three
1 2 3 4
createlists.tcl
脚本的输出。
llength
命令
llength
命令计算列表中的元素数。
#!/usr/bin/tclsh
puts [llength { 1 2 3 4 }]
puts [llength {}]
puts [llength { 1 2 {3 4} }]
puts [llength { 1 2 {} 3 4 }]
该脚本计算四个列表的长度。
puts [llength { 1 2 3 4 }]
该列表包含四个元素,因此,有 4 个被打印到控制台。
puts [llength {}]
此列表为空; llength
命令返回 0。
puts [llength { 1 2 {3 4} }]
此列表包含一个内部列表– {3 4}。 内部列表占一个元素。
puts [llength { 1 2 {} 3 4 }]
空列表也计入一个元素。
$ ./list_length.tcl
4
0
3
5
list_length.tcl
示例的输出。
检索元素
列表元素检索有三个基本命令:lindex
,lrange
和lassign
。
#!/usr/bin/tclsh
set vals { 2 4 6 8 10 12 14 }
puts [lindex $vals 0]
puts [lindex $vals 3]
puts [lindex $vals end]
puts [lindex $vals end-2]
该代码示例使用lindex
命令从指定索引处的列表中检索元素。
puts [lindex $vals 0]
puts [lindex $vals 3]
Tcl 列表索引从 0 开始。上述命令在位置 1 和 4 处打印列表的元素。
puts [lindex $vals end]
puts [lindex $vals end-2]
end
字符串表示最后一个元素的索引。 也可以从中减去一个整数。
$ ./retrieving.tcl
2
8
14
10
这是retrieving.tcl
脚本的输出。
下一个代码示例说明lrange
和lassign
命令。
#!/usr/bin/tclsh
puts [lrange { a b c d e } 2 4]
puts [lrange { a b c d e } 1 end]
lassign { a b c } x y z
puts "$x $y $z"
lrange
命令返回由两个索引指定的列表的一部分。 lassign
命令将列表中的值分配给指定的变量。
puts [lrange { a b c d e } 2 4]
puts [lrange { a b c d e } 1 end]
在这里,我们打印列表的两个子列表。
lassign { a b c } x y z
puts "$x $y $z"
使用lassign
命令,我们将列表元素分配给三个变量。
$ ./retrieving2.tcl
c d e
b c d e
a b c
这是retrieving2.tcl
脚本的输出。
遍历列表
现在我们已经定义了列表和基本的列表操作,我们想遍历列表元素。 我们展示了几种浏览列表项的方法。
#!/usr/bin/tclsh
foreach item {1 2 3 4 5 6 7 8 9} {
puts $item
}
我们使用foreach
命令浏览列表元素。 item
变量的每个循环周期都有数字列表中的下一个值。
$ ./traverse1.tcl
1
2
3
4
5
6
7
8
9
示例的输出。
在第二个示例中,我们将使用while
循环查看日期名称。
#!/usr/bin/tclsh
set days [list Monday Tuesday Wednesday Thursday \
Friday Saturday Sunday]
set n [llength $days]
set i 0
while {$i < $n} {
puts [lindex $days $i]
incr i
}
我们使用while
循环遍历列表。 在使用while
循环时,我们还需要一个计数器和列表中的项目数。
set days [list Monday Tuesday Wednesday Thursday \
Friday Saturday Sunday]
list
命令用于创建日期列表。
set n [llength $days]
列表的长度由llength
命令确定。
set i 0
这是一个柜台。
while {$i < $n} {
puts [lindex $days $i]
incr i
}
while
循环执行主体中的命令,直到计数器等于列表中的元素数为止。
puts [lindex $days $i]
lindex
从计数器指向的列表中返回一个值。
incr i
计数器增加。
$ ./traverse2.tcl
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
示例的输出。
lmap
命令
可以使用lmap
命令浏览列表中的元素。 这是一个函数式命令。 lmap
命令遍历一个或多个列表中的所有元素并收集结果。
#!/usr/bin/tclsh
set vals { 1 2 3 4 5 6 }
puts [lmap a $vals {expr {$a ** 2}}]
该示例将lmap
应用于整数列表。
puts [lmap a $vals {expr {$a ** 2}}]
函数式的lmap
命令将表达式在其主体中应用于vals
列表的每个元素。 返回包含新的平方整数列表的结果。
$ ./lmap_cmd.tcl
1 4 9 16 25 36
示例的输出。
插入元素
下一个示例将元素插入 Tcl 列表。 lappend
命令将元素添加到列表的末尾; 它修改了原始列表。 linsert
命令在指定的索引处插入元素; 它不会修改原始列表,但会返回一个新列表。
#!/usr/bin/tclsh
set nums {4 5 6}
puts $nums
lappend nums 7 8 9
puts $nums
puts [linsert $nums 0 1 2 3]
puts $nums
我们列出了三个数字。
lappend nums 7 8 9
lappend
将数据附加到列表。 原始列表已更改。
puts [linsert $nums 0 1 2 3]
linsert
在给定索引处插入元素。 第一个数字是索引。 其余值是要插入列表中的数字。 该命令创建一个新列表并返回它; 它不会修改原始列表。
$ ./inserting.tcl
4 5 6
4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
4 5 6 7 8 9
这是operations.tcl
脚本的输出。
在下面的示例中,我们将连接列表,搜索项目并替换列表中的项目。
#!/usr/bin/tclsh
set animals1 { lion eagle elephant dog cat }
set animals2 { giraffe tiger horse dolphin }
set animals [concat $animals1 $animals2]
puts $animals
puts [lsearch -exact $animals eagle]
puts [lreplace $animals 3 4 buffalo crocodile]
我们定义了两个动物列表。 我们介绍了三个新命令。
set animals [concat $animals1 $animals2]
concat
命令用于连接(添加)两个列表。 上一行连接了两个列表,新列表设置为animal
变量。
puts [lsearch -exact $animals eagle]
使用lsearch
命令,我们在列表中寻找鹰。 使用-exact
选项,我们可以寻找完全匹配的内容。 该命令返回第一个匹配元素的索引,如果没有匹配,则返回 -1。
puts [lreplace $animals 3 4 buffalo crocodile]
lreplace
命令用水牛和鳄鱼代替了狗和猫。
$ ./operations2.tcl
lion eagle elephant dog cat giraffe tiger horse dolphin
1
lion eagle elephant buffalo crocodile giraffe tiger horse dolphin
这是示例输出。
排序项目
在本节中,我们将展示如何对 Tcl 列表中的项目进行排序。
#!/usr/bin/tclsh
set names { John Mary Lenka Veronika Julia Robert }
set nums { 1 5 4 3 6 7 9 2 11 0 8 2 3 }
puts [lsort $names]
puts [lsort -ascii $names]
puts [lsort -ascii -decreasing $names]
puts [lsort -integer -increasing $nums]
puts [lsort -integer -decreasing $nums]
puts [lsort -integer -unique $nums]
要对列表元素进行排序,我们可以使用sort
命令。 该命令不会修改原始列表。 它返回一个新的元素排序列表。
set names { John Mary Lenka Veronika Julia Robert }
set nums { 1 5 4 3 6 7 9 2 11 0 8 2 3 }
我们有两个列表。 在第一个数字中,我们有字符串,在第二个数字中。
puts [lsort $names]
puts [lsort -ascii $names]
默认排序是 ASCII 排序。 元素按其在 ASCII 表中的位置排序。
puts [lsort -integer -increasing $nums]
puts [lsort -integer -decreasing $nums]
我们将值视为整数,然后按升序和降序对其进行排序。
puts [lsort -integer -unique $nums]
我们在数字上下文中以递增顺序对列表中的元素进行排序。 重复项将被删除。
$ ./sorting.tcl
John Julia Lenka Mary Robert Veronika
John Julia Lenka Mary Robert Veronika
Veronika Robert Mary Lenka Julia John
0 1 2 2 3 3 4 5 6 7 8 9 11
11 9 8 7 6 5 4 3 3 2 2 1 0
0 1 2 3 4 5 6 7 8 9 11
这是sorting.tcl
脚本的输出。
嵌套列表
在 Tcl 中可以有嵌套列表-其他列表中的列表。
#!/usr/bin/tclsh
set nums {1 2 {1 2 3 4} {{1 2} {3 4}} 3 4}
puts [llength $nums]
puts [llength [lindex $nums 2]]
puts [lindex $nums 0]
puts [lindex [lindex $nums 2] 1]
puts [lindex [lindex [lindex $nums 3] 1] 1]
这是一个在 Tcl 中具有嵌套列表的简单示例。
set nums {1 2 {1 2 3 4} {{1 2} {3 4}} 3 4}
nums
是带有两个嵌套列表的列表。 第二个嵌套列表还有两个附加的内部嵌套列表。
puts [llength $nums]
我们确定列表的大小。 嵌套列表被视为一个元素。
puts [llength [lindex $nums 2]]
在此行中,我们确定第一个嵌套列表的大小,这是主列表的第三个元素。
puts [lindex $nums 0]
在这里,我们打印主列表的第一个元素。
puts [lindex [lindex $nums 2] 1]
在上一行中,我们获得了第一个嵌套列表的第二个元素。
puts [lindex [lindex [lindex $nums 3] 1] 1]
在这里,我们获得内部列表的第二个内部列表的第二个元素,它们位于主列表的第 4 个位置。 换句话说:最里面的命令首先执行。 [lindex $nums 3]
返回{{1 2} {3 4}}
。 现在,第二个命令在此返回列表上运行。 [lindex {{1 2} {3 4}} 1]
表达式返回{3 4}
。 最后,最后一条命令[lindex {3 4} 1]
返回 4,该命令被打印到终端上。
$ ./nestedlists.tcl
6
4
1
2
4
示例的输出。
可以使用更简单的语法来检索嵌套列表的元素。
#!/usr/bin/tclsh
set nums { 1 2 {1 2 3 {4 5}} 3 4 }
puts [lindex $nums 0]
puts [lindex $nums 2 1]
puts [lindex $nums 2 3 1]
索引跟随lindex
命令的第一个参数,从最外面的列表的索引开始。
$ ./nestedlists2.tcl
1
2
5
示例的输出。
在 Tcl 教程的这一部分中,我们介绍了 Tcl 列表。
{% endraw %}
Tcl 中的数组
在 Tcl 编程教程的这一部分中,我们将介绍数组。 我们将初始化数组并从中读取数据。
Tcl 数组是变量的集合。 每个变量可以保存任何值,并且该数组由任意值索引。 键值对是无序的。 Tcl 数组是关联数组。
创建数组
可以使用set
或array set
命令创建 Tcl 数组。
#!/usr/bin/tclsh
set names(1) Jane
set names(2) Tom
set names(3) Elisabeth
set names(4) Robert
set names(5) Julia
set names(6) Victoria
puts [array exists names]
puts [array size names]
puts $names(1)
puts $names(2)
puts $names(6)
我们创建一个名为names
的数组。 数字是键,名称是数组的值。
set names(1) Jane
在这一行中,我们为数组键 1 设置了一个值Jane
。我们以后可以通过键引用该值。
puts [array exists names]
array exists
命令确定提供的参数是否为数组。
puts [array size names]
我们使用array size
命令获得数组的大小。
puts $names(1)
我们通过键访问数组中的一个值。
$ ./names.tcl
1
6
Jane
Tom
Victoria
示例的输出。
在第二个示例中,使用array set
命令创建一个数组。
#!/usr/bin/tclsh
array set days {
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
}
set n [array size days]
puts $days(1)
puts "array has $n elements"
我们创建一个days
数组。 它具有 7 个键值对。
$ ./days.tcl
Monday
array has 7 elements
示例输出。
数组是变量的集合
与列表或字典不同,数组中的项目是变量。 这意味着我们可以引用它们。
#!/usr/bin/tclsh
array set days {
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
}
upvar #0 days(1) mon
upvar #0 days(2) tue
upvar #0 days(3) wed
puts $mon
puts $tue
puts $wed
在脚本中,upvar
命令引用了days
数组的三个变量。
upvar #0 days(1) mon
mon
变量引用以 1 索引的变量。upvar
命令的第一个参数是上层,其中#0
表示顶层。 也就是说,days
数组和mon
变量都位于同一全局名称空间中。
puts $mon
在这里,我们通过mon
变量引用days
数组的项。
$ ./colvar.tcl
Monday
Tuesday
Wednesday
示例输出。
数组获取命令
array get
命令返回一个包含数组元素对的列表。
#!/usr/bin/tclsh
array set days {
Peter 34
Jane 17
Lucy 28
Mark 43
Anthony 36
}
puts [array get days]
该示例创建一个数组,并使用array get
命令打印其键值对。
$ ./arrayget.tcl
Peter 34 Anthony 36 Lucy 28 Jane 17 Mark 43
这是arrayget.tcl
脚本的输出。
遍历数组
在以下示例中,我们将展示如何遍历数组。
#!/usr/bin/tclsh
array set days {
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
}
foreach {n day} [array get days] {
puts "$n -> $day"
}
该示例创建一个数组,并使用array get
命令打印其键值对。
foreach {n day} [array get days] {
array get
命令返回键,值元素的列表,可以使用foreach
命令对其进行迭代。
$ ./days2.tcl
4 -> Thursday
5 -> Friday
1 -> Monday
6 -> Saturday
2 -> Tuesday
7 -> Sunday
3 -> Wednesday
这是days2.tcl
脚本的输出。 请注意,成对的元素没有顺序。
以下脚本使用array names
命令遍历数组。
#!/usr/bin/tclsh
array set nums { a 1 b 2 c 3 d 4 e 5 }
puts [array names nums]
foreach n [array names nums] {
puts $nums($n)
}
我们创建一个简单的nums
数组并循环遍历。
array set nums { a 1 b 2 c 3 d 4 e 5 }
我们定义一个简单的数组。
puts [array names nums]
array names
返回一个列表,其中包含数组中所有元素的名称(键)。
foreach n [array names nums] {
puts $nums($n)
}
我们使用键来获取值。
$ ./getnames.tcl
d e a b c
4
5
1
2
3
getnames.tcl
脚本的输出。
前面的示例使用数组的副本,因此不太适合处理大型数组。 数组搜索工具效率更高。
#!/usr/bin/tclsh
array set days {
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
}
set start [array startsearch days]
while {[array anymore days $start]} {
set key [array nextelement days $start]
puts $days($key)
}
array donesearch days $start
我们使用数组搜索命令来迭代一个简单的数组。
set start [array startsearch days]
array startsearch
命令引用数组的开头。
while {[array anymore days $start]} {
如果在数组搜索中还有剩余要处理的元素,则array anymore
命令返回 1。
set key [array nextelement days $start]
array nextelement
命令返回数组中下一个元素的名称。
array donesearch days $start
array donesearch
命令终止数组搜索并破坏与该搜索关联的所有状态。
移除元素
在本章的最后一个示例中,我们将展示如何从数组中删除元素。
#!/usr/bin/tclsh
set names(1) Jane
set names(2) Tom
set names(3) Elisabeth
set names(4) Robert
set names(5) Julia
set names(6) Victoria
puts [array size names]
unset names(1)
unset names(2)
puts [array size names]
我们创建一个names
数组。 我们使用unset
命令从数组中删除项目。 我们在删除两项之前和之后检查数组的大小。
set names(1) Jane
set
命令用于在数组中创建一个项目。
unset names(1)
我们使用unset
命令从数组中删除键为 1 的元素。
$ ./removing.tcl
6
4
一开始,数组中有 6 个元素。 删除两个元素后,剩下 4 个元素。
在 Tcl 教程的这一部分中,我们使用了 Tcl 数组。
Tcl 中的过程
在本教程的这一部分中,我们将介绍 Tcl 过程。
过程是包含一系列命令的代码块。 在许多编程语言中,过程都称为函数。 对于程序仅执行一个特定任务,这是一种良好的编程习惯。 程序为程序带来了模块化。 正确使用程序会带来以下优点:
- 减少代码重复
- 将复杂的问题分解成更简单的部分
- 提高代码的清晰度
- 重用代码
- 信息隐藏
过程有两种基本类型:内置过程和用户定义的过程。 内置过程是 Tcl 核心语言的一部分。 例如,rand()
,sin()
和exp()
是内置程序。 用户定义的过程是使用proc
关键字创建的过程。
proc
关键字用于创建新的 Tcl 命令。 术语过程和命令通常可以互换使用。
我们从一个简单的例子开始。
#!/usr/bin/tclsh
proc tclver {} {
set v [info tclversion]
puts "This is Tcl version $v"
}
tclver
在此脚本中,我们创建一个简单的tclver
过程。 该过程将打印 Tcl 语言的版本。
proc tclver {} {
使用proc
命令创建新过程。 {}
字符表明该过程没有参数。
{
set v [info tclversion]
puts "This is Tcl version $v"
}
这是tclver
过程的主体。 它在我们执行tclver
命令时执行。 该命令的正文位于大括号之间。
tclver
通过指定其名称来调用该过程。
$ ./version.tcl
This is Tcl version 8.6
样本输出。
过程参数
参数是传递给过程的值。 过程可以采用一个或多个参数。 如果过程使用数据,则必须将数据传递给过程。
在下面的示例中,我们有一个采用一个参数的过程。
#!/usr/bin/tclsh
proc ftc {f} {
return [expr $f * 9 / 5 + 32]
}
puts [ftc 100]
puts [ftc 0]
puts [ftc 30]
我们创建一个ftc
程序,将华氏温度转换为摄氏温度。
proc ftc {f} {
该过程采用一个参数。 该程序的主体将使用其名称f
。
return [expr $f * 9 / 5 + 32]
我们计算摄氏温度值。 return
命令将值返回给调用方。 如果该过程未执行显式返回,则其返回值是在过程主体中执行的最后一条命令的值。
puts [ftc 100]
执行ftc
过程。 它以 100 为参数。 这是华氏温度。 返回的值由puts
命令使用,该命令将其打印到控制台。
$ ./fahrenheit.tcl
212
32
86
示例的输出。
接下来,我们将有一个接受两个参数的过程。
#!/usr/bin/tclsh
proc maximum {x y} {
if {$x > $y} {
return $x
} else {
return $y
}
}
set a 23
set b 32
set val [maximum $a $b]
puts "The max of $a, $b is $val"
maximum
过程将两个值的最大值相减。
proc maximum {x y} {
该方法有两个参数。
if {$x > $y} {
return $x
} else {
return $y
}
在这里,我们计算哪个更大。
set a 23
set b 32
我们定义了两个要比较的变量。
set val [maximum $a $b]
我们计算两个变量的最大值。
$ ./maximum.tcl
The max of 23, 32 is 32
这是maximum.tcl
脚本的输出。
可变数量的参数
一个过程可以接受和处理可变数量的参数。 为此,我们使用特殊的args
参数。
#!/usr/bin/tclsh
proc sum {args} {
set s 0
foreach arg $args {
incr s $arg
}
return $s
}
puts [sum 1 2 3 4]
puts [sum 1 2]
puts [sum 4]
我们定义一个sum
过程,将其所有参数加起来。
proc sum {args} {
sum
过程具有一个特殊的args
参数。 它具有传递给该过程的所有值的列表。
foreach arg $args {
incr s $arg
}
我们遍历列表并计算总和。
puts [sum 1 2 3 4]
puts [sum 1 2]
puts [sum 4]
我们调用sum
过程 3 次。 在第一种情况下,它采用 4 个参数,在第二种情况下为 2,在最后一种情况下为 4。
$ ./variable.tcl
10
3
4
variable.tcl
脚本的输出。
隐式参数
Tcl 过程中的参数可能具有隐式值。 如果未提供显式值,则使用隐式值。
#!/usr/bin/tclsh
proc power {a {b 2}} {
if {$b == 2} {
return [expr $a * $a]
}
set value 1
for {set i 0} {$i<$b} {incr i} {
set value [expr $value * $a]
}
return $value
}
set v1 [power 5]
set v2 [power 5 4]
puts "5^2 is $v1"
puts "5^4 is $v2"
在这里,我们创建一个power
过程。 该过程具有一个带有隐式值的参数。 我们可以使用一个和两个参数来调用该过程。
proc power {a {b 2}} {
第二个参数b
具有隐式值 2。如果仅提供一个参数,则power
过程会将a
的值返回到幂 2。
set v1 [power 5]
set v2 [power 5 4]
我们用一个和两个参数调用幂过程。 第一行计算 5 的幂 2。第二行计算 5 的幂 4。
$ ./implicit.tcl
5^2 is 25
5^4 is 625
示例的输出。
返回多个值
return
命令将一个值传递给调用方。 通常需要返回多个值。 在这种情况下,我们可以返回一个列表。
#!/usr/bin/tclsh
proc tworandoms {} {
set r1 [expr round(rand()*10)]
set r2 [expr round(rand()*10)]
return [list $r1 $r2]
}
puts [tworandoms]
puts [tworandoms]
puts [tworandoms]
puts [tworandoms]
我们有一个tworandoms
程序。 它返回 1 到 10 之间的两个随机整数。
set r1 [expr round(rand()*10)]
计算一个随机整数并将其设置为r1
变量。
return [list $r1 $r2]
借助list
命令返回两个值。
$ ./tworandoms.tcl
3 7
1 3
8 7
9 9
样本输出。
递归
在数学和计算机科学中,递归是一种定义函数的方法,其中所定义的函数在其自己的定义内应用。 换句话说,递归函数调用自身以完成其工作。 递归是解决许多编程任务的一种广泛使用的方法。 递归是诸如 Scheme,OCalm 或 Clojure 之类的函数式语言的基本方法。
递归调用在 Tcl 中有一个限制。 递归调用不能超过 1000 个。
递归的典型示例是阶乘的计算。 阶乘n!
是所有小于或等于 n 的正整数的乘积。
#!/usr/bin/tclsh
proc factorial n {
if {$n==0} {
return 1
} else {
return [expr $n * [factorial [expr $n - 1]]]
}
}
# Stack limit between 800 and 1000 levels
puts [factorial 4]
puts [factorial 10]
puts [factorial 18]
在此代码示例中,我们计算三个数字的阶乘。
return [expr $n * [factorial [expr $n - 1]]]
在factorial
过程的主体内部,我们使用修改后的参数调用factorial
过程。 该过程将自行调用。
$ ./recursion.tcl
24
3628800
6402373705728000
这些就是结果。 如果我们尝试计算 100 的阶乘,则会收到“嵌套求值过多”的错误。
作用域
在过程内部声明的变量具有过程作用域。 名称的作用域是程序文本的区域,在该区域内可以引用名称声明的实体而无需对该名称进行限定。 在过程内部声明的变量具有过程作用域; 它也称为本地作用域。 该变量仅在此特定过程中有效。
#!/usr/bin/tclsh
proc test {} {
puts "inside procedure"
#puts "x is $x"
set x 4
puts "x is $x"
}
set x 1
puts "outside procedure"
puts "x is $x"
test
puts "outside procedure"
puts "x is $x"
在前面的示例中,我们在测试过程的内部和外部定义了一个x
变量。
set x 4
puts "x is $x"
在测试过程中,我们定义了x
变量。 该变量具有局部作用域,仅在此过程内有效。
set x 1
puts "outside procedure"
puts "x is $x"
我们在过程外部定义x
变量。 它具有全球作用域。 变量没有冲突,因为它们具有不同的作用域。
$ ./scope.tcl
outside procedure
x is 1
inside procedure
x is 4
outside procedure
x is 1
示例的输出。
可以在过程内部更改全局变量。
#!/usr/bin/tclsh
proc test {} {
upvar x y
puts "inside procedure"
puts "y is $y"
set y 4
puts "y is $y"
}
set x 1
puts "outside procedure"
puts "x is $x"
test
puts "outside procedure"
puts "x is $x"
我们定义一个全局x
变量。 我们在测试过程中更改变量。
upvar x y
我们使用upvar
命令通过名称y
引用全局x
变量。
set y 4
我们为本地y
变量分配一个值,并更改全局x
变量的值。
$ ./scope2.tcl
outside procedure
x is 1
inside procedure
y is 1
y is 4
outside procedure
x is 4
从输出中我们可以看到测试过程更改了x
变量。
使用global
命令,我们可以从过程中引用全局变量。
#!/usr/bin/tclsh
proc test {} {
global x
puts "inside test procedure x is $x"
proc nested {} {
global x
puts "inside nested x is $x"
}
}
set x 1
test
nested
puts "outside x is $x"
在上面的示例中,我们有一个测试过程以及在该测试过程中定义的嵌套过程。 我们从这两个过程中都引用了全局x
变量。
global x
puts "inside test procedure x is $x"
使用global
命令,可以引用在测试过程之外定义的全局x
变量。
proc nested {} {
global x
puts "inside nested x is $x"
}
可以创建嵌套过程。 这些是在其他过程中定义的过程。 我们使用global
命令引用全局x
变量。
test
nested
我们称之为测试过程及其嵌套过程。
$ ./scope3.tcl
inside test procedure x is 1
inside nested x is 1
outside x is 1
示例的输出。
在 Tcl 教程的这一部分中,我们介绍了 Tcl 过程。
输入&输出
在本章中,我们将使用 Tcl 中的输入和输出操作。 Tcl 有几个执行 io 的命令。 我们将介绍其中的一些。
Tcl 使用称为通道的对象读取和写入数据。 可以使用open
或socket
命令创建通道。 Tcl 脚本可使用三个标准通道,而无需显式创建它们。 操作系统会为每个新应用自动打开它们。 它们是stdin
,stdout
和stderr
。 脚本使用标准输入stdin
来读取数据。 脚本使用标准输出stdout
写入数据。 脚本使用标准错误stderr
来编写错误消息。
在第一个示例中,我们将使用puts
命令。 它具有以下概要:
puts ?-nonewline? ?channelId? string
channelId
是我们要在其中写入文本的通道。 channelId
是可选的。 如果未指定,则采用默认值stdout
。
#!/usr/bin/tclsh
puts "Message 1"
puts stdout "Message 2"
puts stderr "Message 3"
puts
命令将文本写入通道。
puts "Message 1"
如果未指定channelId
,则默认情况下写入stdout
。
puts stdout "Message 2"
此行与上一行相同。 我们仅明确指定了channelId
。
puts stderr "Message 3"
我们写入标准错误通道。 错误消息默认情况下转到终端。
$ ./printing.tcl
Message 1
Message 2
Message 3
示例输出。
read
命令
read
命令用于从通道读取数据。 可选参数指定要读取的字符数。 如果省略,该命令将从通道读取所有数据,直到最后。
#!/usr/bin/tclsh
set c [read stdin 1]
while {$c != "q"} {
puts -nonewline "$c"
set c [read stdin 1]
}
该脚本从标准输入通道读取一个字符,然后将其写入标准输出,直到遇到q
字符为止。
set c [read stdin 1]
我们从标准输入通道(stdin
)读取一个字符。
while {$c != "q"} {
我们继续读取字符,直到按q
为止。
gets
命令
gets
命令从通道读取下一行,返回行中直到(但不包括)行尾字符的所有内容。
#!/usr/bin/tclsh
puts -nonewline "Enter your name: "
flush stdout
set name [gets stdin]
puts "Hello $name"
该脚本要求用户输入,然后打印一条消息。
puts -nonewline "Enter your name: "
puts
命令用于将消息打印到终端。 -nonewline
选项禁止换行符。
flush stdout
Tcl 缓冲区在内部输出,因此用puts
编写的字符可能不会立即出现在输出文件或设备上。 flush
命令强制输出立即显示。
set name [gets stdin]
gets
命令从通道读取一条线。
$ ./hello.tcl
Enter your name: Jan
Hello Jan
脚本的示例输出。
pwd
和cd
命令
Tcl 具有pwd
和cd
命令,类似于 shell 命令。 pwd
命令返回当前工作目录,cd
命令用于更改工作目录。
#!/usr/bin/tclsh
set dir [pwd]
puts $dir
cd ..
set dir [pwd]
puts $dir
在此脚本中,我们将打印当前工作目录。 然后,我们更改工作目录并再次打印工作目录。
set dir [pwd]
pwd
命令返回当前工作目录。
cd ..
我们将工作目录更改为当前目录的父目录。 我们使用cd
命令。
$ ./cwd.tcl
/home/janbodnar/prog/tcl/io
/home/janbodnar/prog/tcl
样本输出。
glob
命令
Tcl 具有glob
命令,该命令返回与模式匹配的文件的名称。
#!/usr/bin/tclsh
set files [glob *.tcl]
foreach file $files {
puts $file
}
该脚本将所有带有.tcl
扩展名的文件打印到控制台。
set files [glob *.tcl]
glob
命令返回与*.tcl
模式匹配的文件列表。
foreach file $files {
puts $file
}
我们浏览文件列表,并将列表的每个项目打印到控制台。
$ ./globcmd.tcl
attributes.tcl
allfiles.tcl
printing.tcl
hello.tcl
read.tcl
files.tcl
globcmd.tcl
write2file.tcl
cwd.tcl
readfile.tcl
isfile.tcl
addnumbers.tcl
这是globcmd.tcl
脚本的示例输出。
处理文件
file
命令操纵文件名和属性。 它有很多选择。
#!/usr/bin/tclsh
puts [file volumes]
[file mkdir new]
该脚本将打印系统的已安装卷,并创建一个新目录。
puts [file volumes]
file volumes
命令返回到系统上安装的卷的绝对路径。
[file mkdir new]
file mkdir
创建一个名为new
的目录。
$ ./voldir.tcl
/
$ ls -d */
doc/ new/ tmp/
在 Linux 系统上,有一个已安装的卷-根目录。 ls
命令确认new
目录的创建。
在下面的代码示例中,我们将检查文件名是常规文件还是目录。
#!/usr/bin/tclsh
set files [glob *]
foreach fl $files {
if {[file isfile $fl]} {
puts "$fl is a file"
} elseif { [file isdirectory $fl]} {
puts "$fl is a directory"
}
}
我们遍历当前工作目录中的所有文件名,并打印它是文件还是目录。
set files [glob *]
使用glob
命令,我们创建当前目录的文件和目录名称的列表。
if {[file isfile $fl]} {
如果有问题的文件名是文件,我们将执行if
命令的主体。
} elseif { [file isdirectory $fl]} {
file isdirectory
命令确定文件名是否为目录。 请注意,在 Unix 上,目录是文件的特例。
puts
命令可用于写入文件。
#!/usr/bin/tclsh
set fp [open days w]
set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}
puts $fp $days
close $fp
在脚本中,我们打开一个文件进行写入。 我们将一周中的几天写入文件。
set fp [open days w]
我们打开一个名为days
的文件进行写入。 open
命令返回一个通道 ID。
set days {Monday Tuesday Wednesday Thursday Friday Saturday Sunday}
该数据将被写入文件。
puts $fp $days
我们使用open
命令返回的通道 ID 写入文件。
close $fp
我们关闭打开的频道。
$ ./write2file.tcl
$ cat days
Monday Tuesday Wednesday Thursday Friday Saturday Sunday
我们运行脚本并检查days
文件的内容。
在下面的脚本中,我们将从文件中读取数据。
$ cat languages
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme
我们在目录中有一个简单的文件,称为语言。
#!/usr/bin/tclsh
set fp [open languages r]
set data [read $fp]
puts -nonewline $data
close $fp
我们从提供的文件中读取数据,读取其内容,然后将数据打印到终端。
set fp [open languages r]
我们通过以只读模式打开语言文件来创建频道。
set data [read $fp]
如果我们没有为read
命令提供第二个参数,它将从文件中读取所有数据,直到文件结尾。
puts -nonewline $data
我们将数据打印到控制台。
$ ./readfile.tcl
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme
readfile.tcl
脚本的示例运行。
eof
命令检查所提供通道的行尾。
#!/usr/bin/tclsh
set fp [open languages]
while {![eof $fp]} {
puts [gets $fp]
}
close $fp
我们使用eof
命令读取文件的内容。
while {![eof $fp]} {
puts [gets $fp]
}
循环继续进行,直到eof
遇到文件结尾都返回true
为止。 在体内,我们使用gets
命令从文件中读取一行。
$ ./readfile2.tcl
Python
Tcl
Visual Basic
Perl
Java
C
C#
Ruby
Scheme
readfile2.tcl
脚本的示例运行。
下一个脚本将执行一些其他文件操作。
#!/usr/bin/tclsh
set fp [open newfile w]
puts $fp "this is new file"
flush $fp
file copy newfile newfile2
file delete newfile
close $fp
我们打开一个文件,并在其中写入一些文本。 文件被复制。 然后删除原始文件。
file copy newfile newfile2
file copy
命令复制文件。
file delete newfile
原始文件用file delete
命令删除。
在最后一个示例中,我们将使用文件属性。
#!/usr/bin/tclsh
set files [glob *]
set mx 0
foreach fl $files {
set len [string length $fl]
if { $len > $mx} {
set mx $len
}
}
set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]
set fstr "%-$mx\s %d bytes"
foreach fl $files {
set size [file size $fl]
puts [format $fstr $fl $size]
}
该脚本将创建两列。 在第一列中,我们有文件的名称。 在第二列中,我们显示文件的大小。
foreach fl $files {
set len [string length $fl]
if { $len > $mx} {
set mx $len
}
}
在此循环中,我们找出了最长的文件名。 格式化输出列时将使用此格式。
set fstr "%-$mx\s %-s"
puts [format $fstr Name Size]
在这里,我们打印列的标题。 要格式化数据,我们使用format
命令。
set fstr "%-$mx\s %d bytes"
foreach fl $files {
set size [file size $fl]
puts [format $fstr $fl $size]
}
我们遍历文件列表并打印每个文件名及其大小。 file size
命令确定文件的大小。
$ ./attributes.tcl
Name Size
attributes.tcl 337 bytes
newfile2 17 bytes
allfiles.tcl 75 bytes
printing.tcl 83 bytes
languages 51 bytes
hello.tcl 109 bytes
days 57 bytes
read.tcl 113 bytes
files.tcl 140 bytes
globcmd.tcl 82 bytes
write2file.tcl 134 bytes
doc 4096 bytes
cwd.tcl 76 bytes
tmp 4096 bytes
readfile.tcl 98 bytes
isfile.tcl 219 bytes
样品运行。
在本章中,我们介绍了 Tcl 中的输入/输出操作。
AWK 教程
这是 AWK 教程。 它涵盖了 AWK 工具的基础知识。
AWK
AWK 是一种模式扫描和处理语言。 AWK 包含一组针对文本数据流要采取的措施。 AWK 广泛使用正则表达式。 它是大多数类 Unix 操作系统的标准函数。
AWK 于 1977 年在贝尔实验室创立。它的名字取自其作者的姓氏-Alfred Aho,Peter Weinberger 和 Brian Kernighan。
AWK 程序
AWK 程序由一系列模式操作语句和可选的函数定义组成。 它处理文本文件。 AWK 是一种面向行的语言。 它将文件划分为称为记录的行。 每行被分解为字段的序列。 这些字段由特殊变量访问:$1
读取第一个字段,$2
读取第二个字段,依此类推。 $0
变量引用整个记录。
AWK 程序的结构具有以下形式:
pattern { action }
模式是对每个记录执行的测试。 如果满足条件,则执行操作。 模式或动作都可以省略,但不能两者都省略。 默认模式匹配每行,默认操作是打印记录。
awk -f program-file [file-list]
awk program [file-list]
AWK 程序可以通过两种基本方式运行:a)从单独的文件中读取程序; 程序的名称紧随-f
选项,b)程序在命令行中用引号引起来。
AWK 单线
AWK 单线性是从命令行运行的简单单发程序。 让我们有以下文本文件:
$ cat mywords
brown
tree
craftsmanship
book
beautiful
existence
ministerial
computer
town
我们要打印mywords
文件中包含的所有超过五个字符的单词。
$ awk 'length($1) > 5 {print}' mywords
craftsmanship
beautiful
existence
ministerial
computer
AWK 程序位于两个单引号字符之间。 首先是模式; 我们指定记录的长度大于五。 length()
函数返回字符串的长度。 $1
变量引用记录的第一个字段; 在我们的情况下,每条记录只有一个字段。 动作放置在大括号之间。
$ awk 'length($1) > 5' mywords
craftsmanship
beautiful
existence
ministerial
computer
正如我们前面所指定的,该动作可以省略。 在这种情况下,将执行默认操作-打印整个记录。
正则表达式通常应用于 AWK 字段。 ~
是正则表达式匹配运算符。 它检查字符串是否与提供的正则表达式匹配。
$ awk '$1 ~ /^[b,c]/ {print $1}' mywords
brown
craftsmanship
book
beautiful
computer
在此程序中,我们打印所有以b
或c
字符开头的单词。 正则表达式位于两个斜杠字符之间。
AWK 提供重要的内置变量。 例如,NR
是一个内置变量,指向正在处理的当前行。
$ awk 'NR % 2 == 0 {print}' mywords
tree
book
existence
computer
上面的程序每隔mywords
文件打印一次记录。 模除NR
变量,我们得到一条偶数行。
假设我们要打印文件的行号。
$ awk '{print NR, $0}' mywords
1 brown
2 tree
3 craftsmanship
4 book
5 beautiful
6 existence
7 ministerial
8 computer
9 town
同样,我们使用NR
变量。 我们跳过该模式,因此,每一行都执行该操作。 $0
变量引用整个记录。
对于以下示例,我们具有此 C 源文件。
$ cat source.c
1 #include <stdio.h>
2
3 int main(void) {
4
5 char *countries[5] = { "Germany", "Slovakia", "Poland",
6 "China", "Hungary" };
7
8 size_t len = sizeof(countries) / sizeof(*countries);
9
10 for (size_t i=0; i < len; i++) {
11
12 printf("%s\n", countries[i]);
13 }
14 }
碰巧我们复制了一些数据,包括行号。 我们的任务是从文本中删除数字。
$ awk '{print substr($0, 4)}' source.c
#include <stdio.h>
int main(void) {
char *countries[5] = { "Germany", "Slovakia", "Poland",
"China", "Hungary" };
size_t len = sizeof(countries) / sizeof(*countries);
for (size_t i=0; i < len; i++) {
printf("%s\n", countries[i]);
}
}
我们使用substr()
函数。 它从给定的字符串打印一个子字符串。 我们在每行上应用该函数,跳过前三个字符。 换句话说,我们从第四个字符开始打印每个记录直到结束。
开始和结束模式
BEGIN
和END
是在读取所有记录之前和之后执行的特殊模式。 这两个关键字后跟大括号,我们在其中指定要执行的语句。
我们有以下两个文件:
$ cat mywords;
brown
tree
craftsmanship
book
beautiful
existence
ministerial
computer
town
$ cat mywords2;
pleasant
curly
storm
hering
immune
我们想知道这两行中的行数。
$ awk 'END {print NR}' mywords mywords2
14
我们将两个文件传递给 AWK 程序。 AWK 按顺序处理在命令行上收到的文件名。 关键字END
之后的块在程序结尾处执行; 我们打印NR
变量,该变量保存最后处理的行的行号。
$ awk 'BEGIN {srand()} {lines[NR] = $0} END { r=int(rand()*NR + 1); print lines[r]}' mywords
tree
上面的程序从mywords
文件中打印一条随机行。 srand()
函数为随机数生成器提供种子。 该函数仅需执行一次。 在程序的主要部分,我们将当前记录存储到lines
数组中。 最后,我们计算 1 到NR
之间的随机数,并打印从数组结构中随机选择的行。
匹配函数
match()
是内置的字符串操作函数。 它测试给定的字符串是否包含正则表达式模式。 第一个参数是字符串,第二个参数是正则表达式模式。 它类似于~
运算符。
$ awk 'match($0, /^[c,b]/)' mywords
brown
craftsmanship
book
beautiful
computer
程序将打印以c
或b
开头的行。 正则表达式位于两个斜杠字符之间。
match()
函数设置RSTART
变量; 它是匹配模式开始的索引。
$ awk 'match($0, /i/) {print $0 " has i character at " RSTART}' mywords
craftsmanship has i character at 12
beautiful has i character at 6
existence has i character at 3
ministerial has i character at 2
程序将打印出包含i
字符的单词。 此外,它还会打印字符的首次出现。
AWK 内置变量
AWK 有几个内置变量。 它们在运行程序时由 AWK 设置。 我们已经看到了NR
,$0
和RSTART
变量。
$ awk 'BEGIN { print ARGC, ARGV[0], ARGV[1]}' mywords
2 awk mywords
该程序将打印 AWK 程序的参数数量和前两个参数。 ARGC
是命令行参数的数量; 在我们的案例中,有两个论点,包括 AWK 本身。 ARGV
是命令行参数数组。 数组的索引从 0 到ARGC
-1。
FS
是输入字段分隔符,默认为空格。 NF
是当前输入记录中的字段数。
对于以下程序,我们使用此文件:
$ cat values
2, 53, 4, 16, 4, 23, 2, 7, 88
4, 5, 16, 42, 3, 7, 8, 39, 21
23, 43, 67, 12, 11, 33, 3, 6
我们有三行用逗号分隔的值。
stats.awk
BEGIN {
FS=","
max = 0
min = 10**10
sum = 0
avg = 0
}
{
for (i=1; i<=NF; i++) {
sum += $i
if (max < $i) {
max = $i
}
if (min > $i) {
min = $i
}
printf("%d ", $i)
}
}
END {
avg = sum / NF
printf("\n")
printf("Min: %d, Max: %d, Sum: %d, Average: %d\n", min, max, sum, avg)
}
程序将从提供的值中统计基本统计信息。
FS=","
文件中的值用逗号分隔; 因此,我们将FS
变量设置为逗号字符。
max = 0
min = 10**10
sum = 0
avg = 0
我们定义最大值,最小值,总和和平均值的默认值。 AWK 变量是动态的; 它们的值可以是浮点数或字符串,或两者兼有,这取决于它们的使用方式。
{
for (i=1; i<=NF; i++) {
sum += $i
if (max < $i) {
max = $i
}
if (min > $i) {
min = $i
}
printf("%d ", $i)
}
}
在脚本的主要部分,我们遍历每一行并计算值的最大值,最小值和总和。 NF
用于确定每行的值数量。
END {
avg = sum / NF
printf("\n")
printf("Min: %d, Max: %d, Sum: %d, Average: %d\n", min, max, sum, avg)
}
在脚本的最后,我们计算平均值并将计算结果打印到控制台。
$ awk -f stats.awk values
2 53 4 16 4 23 2 7 88 4 5 16 42 3 7 8 39 21 23 43 67 12 11 33 3 6
Min: 2, Max: 88, Sum: 542, Average: 67
这是stats.awk
程序的输出。
可以使用-F
标志将FS
变量指定为命令行选项。
$ awk -F: '{print $1, $7}' /etc/passwd | head -7
root /bin/bash
daemon /usr/sbin/nologin
bin /usr/sbin/nologin
sys /usr/sbin/nologin
sync /bin/sync
games /usr/sbin/nologin
man /usr/sbin/nologin
该示例从系统/etc/passwd
文件中打印第一个(用户名)和第七个字段(用户的外壳程序)。 head
命令仅用于打印前七行。 /etc/passwd
文件中的数据用冒号分隔。 因此,冒号被赋予-F
选项。
RS
是输入记录分隔符,默认情况下是换行符。
$ echo "Jane 17#Tom 23#Mark 34" | awk 'BEGIN {RS="#"} {print $1, "is", $2, "years old"}'
Jane is 17 years old
Tom is 23 years old
Mark is 34 years old
在示例中,我们用#
字符分隔了相关数据。 RS
用于剥离它们。 AWK 可以从echo
之类的其他命令接收输入。
将变量传递给 AWK
AWK 具有-v
选项,用于为变量分配值。 对于下一个程序,我们具有text
文件:
$ cat text
The French nation, oppressed, degraded during many centuries
by the most insolent despotism, has finally awakened to a
consciousness of its rights and of the power to which its
destinies summon it.
mygrep.awk
{
for (i=1; i<=NF; i++) {
field = $i
if (field ~ word) {
c = index($0, field)
print NR "," c, $0
next
}
}
}
该示例模拟grep
工具。 它找到提供的单词并打印其行和起始索引。 (程序仅找到单词的第一个出现。)使用-v
选项将word
变量传递给程序。
$ awk -f mygrep.awk -v word=the text
2,4 by the most insolent despotism, has finally awakened to a
3,36 consciousness of its rights and of the power to which its
我们在text
文件中寻找了"the"
字样。
管道
AWK 可以通过管道接收输入并将输出发送到其他命令。
$ echo -e "1 2 3 5\n2 2 3 8" | awk '{print $(NF)}'
5
8
在这种情况下,AWK 从echo
命令接收输出。 打印最后一列的值。
$ awk -F: '$7 ~ /bash/ {print $1}' /etc/passwd | wc -l
3
在此,AWK 程序通过管道将数据发送到wc
命令。 在 AWK 程序中,我们找出使用 bash 的用户。 它们的名称被传递给wc
命令,该命令对其进行计数。 在我们的例子中,有三个用户使用 bash。
拼写检查
我们创建一个用于拼写检查的 AWK 程序。
spellcheck.awk
BEGIN {
count = 0
i = 0
while (getline myword <"/usr/share/dict/words") {
dict[i] = myword
i++
}
}
{
for (i=1; i<=NF; i++) {
field = $i
if (match(field, /[[:punct:]]$/)) {
field = substr(field, 0, RSTART-1)
}
mywords[count] = field
count++
}
}
END {
for (w_i in mywords) {
for (w_j in dict) {
if (mywords[w_i] == dict[w_j] ||
tolower(mywords[w_i]) == dict[w_j]) {
delete mywords[w_i]
}
}
}
for (w_i in mywords) {
if (mywords[w_i] != "") {
print mywords[w_i]
}
}
}
该脚本将提供的文本文件的单词与字典进行比较。 在标准/usr/share/dict/words
路径下,我们可以找到英语词典; 每个单词在单独的行上。
BEGIN {
count = 0
i = 0
while (getline myword <"/usr/share/dict/words") {
dict[i] = myword
i++
}
}
在BEGIN
块内部,我们将字典中的单词读入dict
数组。 getline
命令从给定的文件名中读取一条记录; 记录存储在$0
变量中。
{
for (i=1; i<=NF; i++) {
field = $i
if (match(field, /[[:punct:]]$/)) {
field = substr(field, 0, RSTART-1)
}
mywords[count] = field
count++
}
}
在程序的主要部分,我们将要进行拼写检查的文件的单词放入mywords
数组。 我们会删除单词结尾处的所有标点符号(例如逗号或点)。
END {
for (w_i in mywords) {
for (w_j in dict) {
if (mywords[w_i] == dict[w_j] ||
tolower(mywords[w_i]) == dict[w_j]) {
delete mywords[w_i]
}
}
}
...
}
我们将mywords
数组中的单词与字典数组进行比较。 如果单词在词典中,则使用delete
命令将其删除。 以句子开头的单词以大写字母开头; 因此,我们还利用tolower()
函数检查小写字母的替换形式。
for (w_i in mywords) {
if (mywords[w_i] != "") {
print mywords[w_i]
}
}
在词典中找不到其余的单词; 它们被打印到控制台。
$ awk -f spellcheck.awk text
consciosness
finaly
我们已经在文本文件上运行了该程序; 我们发现了两个拼写错误的单词。 请注意,该程序需要一些时间才能完成。
剪刀石头布
剪刀石头布是一种流行的手形游戏,其中每个玩家都用伸出的手同时形成三个形状之一。 我们在 AWK 中创建此游戏。
rock_scissors_paper.awk
# This program creates a rock-paper-scissors game.
BEGIN {
srand()
opts[1] = "rock"
opts[2] = "paper"
opts[3] = "scissors"
do {
print "1 - rock"
print "2 - paper"
print "3 - scissors"
print "9 - end game"
ret = getline < "-"
if (ret == 0 || ret == -1) {
exit
}
val = $0
if (val == 9) {
exit
} else if (val != 1 && val != 2 && val != 3) {
print "Invalid option"
continue
} else {
play_game(val)
}
} while (1)
}
function play_game(val) {
r = int(rand()*3) + 1
print "I have " opts[r] " you have " opts[val]
if (val == r) {
print "Tie, next throw"
return
}
if (val == 1 && r == 2) {
print "Paper covers rock, you loose"
} else if (val == 2 && r == 1) {
print "Paper covers rock, you win"
} else if (val == 2 && r == 3) {
print "Scissors cut paper, you loose"
} else if (val == 3 && r == 2) {
print "Scissors cut paper, you win"
} else if (val == 3 && r == 1) {
print "Rock blunts scissors, you loose"
} else if (val == 1 && r == 3) {
print "Rock blunts scissors, you win"
}
}
我们在电脑上玩游戏,电脑会随机选择选项。
srand()
我们使用srand()
函数为随机数生成器播种。
opts[1] = "rock"
opts[2] = "paper"
opts[3] = "scissors"
这三个选项存储在opts
数组中。
do {
print "1 - rock"
print "2 - paper"
print "3 - scissors"
print "9 - end game"
...
游戏的周期由do-while
循环控制。 首先,将选项打印到终端。
ret = getline < "-"
if (ret == 0 || ret == -1) {
exit
}
val = $0
我们选择的值是使用getline
命令从命令行读取的; 该值存储在val
变量中。
if (val == 9) {
exit
} else if (val != 1 && val != 2 && val != 3) {
print "Invalid option"
continue
} else {
play_game(val)
}
如果选择选项 9,则退出程序。如果该值在打印的菜单选项之外,则打印错误消息,并使用continue
命令开始新的循环。 如果我们正确选择了三个选项之一,则调用play_game()
函数。
r = int(rand()*3) + 1
使用rand()
函数从1..3
中选择一个随机值。 这是计算机的选择。
if (val == r) {
print "Tie, next throw"
return
}
如果两个玩家选择相同的选项,则平局。 我们从函数返回并开始新的循环。
if (val == 1 && r == 2) {
print "Paper covers rock, you loose"
} else if (val == 2 && r == 1) {
...
我们比较所选播放器的值,并将结果打印到控制台。
$ awk -f rock_scissors_paper.awk
1 - rock
2 - paper
3 - scissors
9 - end game
1
I have scissors you have rock
Rock blunts scissors, you win
1 - rock
2 - paper
3 - scissors
9 - end game
3
I have paper you have scissors
Scissors cut paper, you win
1 - rock
2 - paper
3 - scissors
9 - end game
游戏示例。
标记关键字
在下面的示例中,我们在源文件中标记 Java 关键字。
mark_keywords.awk
# the program adds tags around Java keywords
# it works on keywords that are separate words
BEGIN {
# load java keywords
i = 0
while (getline kwd <"javakeywords2") {
keywords[i] = kwd
i++
}
}
{
mtch = 0
ln = ""
space = ""
# calculate the beginning space
if (match($0, /[^[:space:]]/)) {
if (RSTART > 1) {
space = sprintf("%*s", RSTART, "")
}
}
# add the space to the line
ln = ln space
for (i=1; i <= NF; i++) {
field = $i
# go through keywords
for (w_i in keywords) {
kwd = keywords[w_i]
# check if a field is a keyword
if (field == kwd) {
mtch = 1
}
}
# add tags to the line
if (mtch == 1) {
ln = ln "<kwd>" field "</kwd> "
} else {
ln = ln field " "
}
mtch = 0
}
print ln
}
该程序在它识别的每个关键字周围添加<kwd>
和</kwd>
标签。 这是一个基本示例; 它适用于单独单词的关键字。 它没有解决更复杂的结构。
# load java keywords
i = 0
while (getline kwd <"javakeywords2") {
keywords[i] = kwd
i++
}
我们从文件中加载 Java 关键字; 每个关键字在单独的行上。 关键字存储在keywords
数组中。
# calculate the beginning space
if (match($0, /[^[:space:]]/)) {
if (RSTART > 1) {
space = sprintf("%*s", RSTART, "")
}
}
使用正则表达式,我们计算行首的空格(如果有)。 space
是一个字符串变量,等于当前行的空格宽度。 计算空间是为了保持程序缩进。
# add the space to the line
ln = ln space
将空格添加到ln
变量中。 在 AWK 中,我们使用空格添加字符串。
for (i=1; i <= NF; i++) {
field = $i
...
}
我们遍历当前行的字段; 该字段存储在field
变量中。
# go through keywords
for (w_i in keywords) {
kwd = keywords[w_i]
# check if a field is a keyword
if (field == kwd) {
mtch = 1
}
}
在for
循环中,我们遍历 Java 关键字,并检查字段是否为 Java 关键字。
# add tags to the line
if (mtch == 1) {
ln = ln "<kwd>" field "</kwd> "
} else {
ln = ln field " "
}
如果有关键字,我们将标签附加在关键字周围; 否则,我们只是将字段附加到该行。
print ln
构建的行将打印到控制台。
$ awk -f markkeywords2.awk program.java
<kwd>package</kwd> com.zetcode;
<kwd>class</kwd> Test {
<kwd>int</kwd> x = 1;
<kwd>public</kwd> <kwd>void</kwd> exec1() {
System.out.println(this.x);
System.out.println(x);
}
<kwd>public</kwd> <kwd>void</kwd> exec2() {
<kwd>int</kwd> z = 5;
System.out.println(x);
System.out.println(z);
}
}
<kwd>public</kwd> <kwd>class</kwd> MethodScope {
<kwd>public</kwd> <kwd>static</kwd> <kwd>void</kwd> main(String[] args) {
Test ts = <kwd>new</kwd> Test();
ts.exec1();
ts.exec2();
}
}
在小型 Java 程序上运行的示例。
这是 AWK 教程。
# Vaadin 教程
Vaadin 框架介绍
本教程介绍 Vaadin Web 框架。
Vaadin 框架
Vaadin 是用于构建单页 Web 应用的流行 Java 框架。 它由一家专门从事富 Internet 应用设计和开发的芬兰公司开发。 估计有 15 万开发者使用 Vaadin。 它的开发始于 2002 年。
Vaadin 框架特性
以下是 Vaadin 特性的列表:
- 这是一个 Java Web 框架
- 概念上类似于 Java Swing
- 允许构建不带 HTML 和 JavaScript 的 Web 应用
- 基于 Java Servlet 构建
- 包含大量组件和窗口
- 提供可定制的外观
- 在客户端和服务器之间使用异步消息
- 使用服务器端编程模型
- 具有 Eclipse,NetBeans 和 IntelliJ 的插件
- 使用 Google Web Toolkit 呈现结果网页
- 提升代码质量和安全性
Vaadin NetBeans
NetBeans 包含一个用于创建 Vaadin 应用的插件。 我们转到工具/插件并安装 Vaadin 插件。 然后,我们重新启动 NetBeans。
图:Vaadin 新项目
安装 Vaadin 插件后,NetBeans 中将出现一个新的 Vaadin 项目类别。 要创建一个新的 Vaadin 项目,我们转到File / New Project / Vaadin
并选择Vaadin Web Application Project
。
Vaadin 简单示例
以下是一个简单的 Vaadin 应用,该应用使用 Vaadin Label
组件显示文本。
$ tree
.
├── nb-configuration.xml
├── pom.xml
├── README.md
└── src
└── main
├── java
│ └── com
│ └── zetcode
│ └── simple
│ └── MyUI.java
├── resources
│ └── README
└── webapp
├── META-INF
│ └── context.xml
└── VAADIN
└── themes
└── mytheme
├── addons.scss
├── favicon.ico
├── mytheme.scss
└── styles.scss
这是 Vaadin Web 应用的项目结构。 请注意为我们自动创建的主题。
MyUI.java
package com.zetcode.simple;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
Label lbl = new Label("Simple example");
lbl.setDescription("This is a Label component");
layout.addComponents(lbl);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
MyUI
是应用入口点。 UI 可以是浏览器窗口(或选项卡),也可以是嵌入 Vaadin 应用的 HTML 页面的一部分。
@Theme("mytheme")
使用@Theme
批注,我们指定应用的主题。 系统会自动为我们创建一个默认的mytheme
。
VerticalLayout layout = new VerticalLayout();
Vaadin 中的组件放置在布局管理器中。 VerticalLayout
将组件放在一列中。 由于我们只有一个Label
组件,因此选择哪个布局管理器都没关系。
Label lbl = new Label("Simple example");
创建了一个新的Label
组件。 标签显示不可编辑的文本。
lbl.setDescription("This is a Label component");
通过setDescription()
方法,我们为标签组件添加了一个工具提示。
layout.addComponents(lbl);
标签通过addComponents()
方法添加到布局中。
setContent(layout);
最后,使用setContent()
将布局添加到 UI。
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
从此代码摘录中可以看到,Vaadin 框架是基于 Java Servlet 技术构建的。
图:Vaadin Label
在屏幕截图中,我们可以看到Label
组件及其工具提示。
在本教程中,我们介绍了 Vaadin 框架。 您可能也对相关教程感兴趣: Vaadin Button
教程, Vaadin CheckBox
教程, Vaadin ComboBox
教程, Vaadin TextArea
教程, Vaadin 滑块教程, Java 教程。
Vaadin Grid
教程
在 Vaadin Grid
教程中,我们学习了 Vaadin Grid
组件的基础知识。 Vaadin 网格示例显示网格中来自 MySQL 数据库的数据。
Vaadin 框架
Vaadin 是一种流行的 Java Web 开发框架,用于构建单页 Web 应用。
Vaadin Grid
Grid
组件用于显示和编辑以行和列排列的表格数据。
Vaadin Grid
示例
以下示例演示了 Vaadin Grid
组件的用法。 我们从 MySQL 数据库加载数据并将其显示在Grid
组件中。
NetBeans IDE 可用于轻松创建 Vaadin Web 应用。 我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
我们向 POM 文件添加了两个依赖关系:MySQL 驱动程序和JdbcTemplate
库。 JdbcTemplate
简化了 Java 中的数据库编程。
cars_mysql.java
-- SQL for the Cars table
CREATE TABLE Cars(Id BIGINT PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(150), Price INTEGER);
INSERT INTO Cars(Name, Price) VALUES('Audi', 52642);
INSERT INTO Cars(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO Cars(Name, Price) VALUES('Skoda', 9000);
INSERT INTO Cars(Name, Price) VALUES('Volvo', 29000);
INSERT INTO Cars(Name, Price) VALUES('Bentley', 350000);
INSERT INTO Cars(Name, Price) VALUES('Citroen', 21000);
INSERT INTO Cars(Name, Price) VALUES('Hummer', 41400);
INSERT INTO Cars(Name, Price) VALUES('Volkswagen', 21600);
这是 MySQL 数据库表的数据。
MyUI.java
package com.zetcode.main;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.zetcode.bean.Car;
import com.zetcode.service.ReadCars;
import java.util.List;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout vbox = new VerticalLayout();
HorizontalLayout hbox = new HorizontalLayout();
Grid grid = new Grid();
grid.addColumn("Id", Long.class);
grid.addColumn("Name", String.class);
grid.addColumn("Price", Integer.class);
Button loadButton = new Button("Load data");
loadButton.addClickListener(e -> {
grid.getContainerDataSource().removeAllItems();
List<Car> cars = ReadCars.read();
for (Car car : cars) {
grid.addRow(car.getId(), car.getName(), car.getPrice());
}
});
Button clearButton = new Button("Clear data");
clearButton.addClickListener(e -> {
grid.getContainerDataSource().removeAllItems();
});
vbox.addComponents(grid);
hbox.addComponents(loadButton, clearButton);
hbox.setSpacing(true);
vbox.addComponent(hbox);
vbox.setMargin(true);
vbox.setSpacing(true);
setContent(vbox);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
这是MyUI
类。 它是应用的入口点。 我们有一个Grid
组件和两个按钮。 一个按钮从数据库中加载数据并将其插入Grid
,另一按钮从Grid
中删除数据。
VerticalLayout vbox = new VerticalLayout();
HorizontalLayout hbox = new HorizontalLayout();
要创建布局,我们使用HorizontalLayout
和VerticalLayout
。
Grid grid = new Grid();
Grid
组件已创建。
grid.addColumn("Id", Long.class);
grid.addColumn("Name", String.class);
grid.addColumn("Price", Integer.class);
使用addColumn()
方法,我们向Grid
添加新列。 方法的第二个参数是列的日期类型。
Button loadButton = new Button("Load data");
loadButton.addClickListener(e -> {
grid.getContainerDataSource().removeAllItems();
List<Car> cars = ReadCars.read();
for (Car car : cars) {
grid.addRow(car.getId(), car.getName(), car.getPrice());
}
});
加载按钮从数据库加载数据并将其插入Grid
。 数据库工作委托给ReadCars.read()
方法。 使用addRow()
方法将新行添加到网格中。
Button clearButton = new Button("Clear data");
clearButton.addClickListener(e -> {
grid.getContainerDataSource().removeAllItems();
});
清除按钮将从Grid
中删除所有数据。 removeAllItems()
方法可以完成这项工作。
vbox.addComponents(grid);
hbox.addComponents(loadButton, clearButton);
组件将添加到布局管理器。
Car.java
package com.zetcode.bean;
public class Car {
private Long id;
private String name;
private int price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
这是Car
bean; 它包含id
,name
和price
属性。
ReadCars.java
package com.zetcode.service;
import com.zetcode.bean.Car;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
public class ReadCars {
public static List<Car> read() {
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=true";
SimpleDriverDataSource ds = new SimpleDriverDataSource();
try {
ds.setDriver(new com.mysql.jdbc.Driver());
} catch (SQLException ex) {
Logger.getLogger(ReadCars.class.getName()).log(Level.SEVERE, null, ex);
}
ds.setUsername("testuser");
ds.setPassword("test623");
ds.setUrl(url);
String sql = "SELECT * FROM Cars";
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
List<Car> cars = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Car.class));
return cars;
}
}
ReadCars
包含连接到 MySQL 数据库并从Cars
表中检索所有汽车的代码。 我们使用 Spring 的JdbcTemplate
来完成这项工作。
String url = "jdbc:mysql://localhost:3306/testdb?useSSL=true";
这是 MySQL 数据库的连接 URL。 数据库名称为testdb
。
String sql = "SELECT * FROM Cars";
该 SQL 语句从Cars
表中选择所有行。
JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
List<Car> cars = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Car.class));
在BeanPropertyRowMapper
的帮助下,该工具自动将结果集列映射到Car
类的属性,我们从Cars
表中检索所有行并将其映射到Car
对象的列表。
return cars;
汽车列表将返回给调用者。
图:Vaadin Grid
在本教程中,我们学习了 Vaadin Grid
组件的基础知识。 您可能也对相关教程感兴趣: Vaadin TextArea
教程, Vaadin ComboBox
教程, Vaadin Slider
教程, Vaadin CheckBox
教程, Java 教程。
Vaadin TextArea
教程
在 Vaadin TextArea
教程中,我们学习 Vaadin TextArea
组件的基础。 Vaadin TextArea
示例显示TextArea
组件中网页的内容。 该示例使用 Jsoup 库读取网页内容。
Vaadin
Vaadin 是一种流行的 Java Web 开发框架,用于构建单页 Web 应用。
Vaadin TextArea
TextArea
是显示纯文本的多行文本区域组件。
Vaadin TextArea
示例
以下程序演示了 Vaadin TextArea
组件的用法。 它从网页中读取 HTML 内容,该内容在附近的TextField
组件中指定。
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
我们需要将jsoup
依赖项添加到 Maven POM 文件中。 Jsoup 是一种流行的 Java HTML 解析器。
MyUI.java
package com.zetcode.main;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.zetcode.service.ReadPage;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
TextArea area = new TextArea();
area.setWidth("650");
area.setHeight("350");
TextField field = new TextField("Enter an URL");
field.setWidth("350");
Button button = new Button("Submit");
button.addClickListener(e -> {
String content = ReadPage.read(field.getValue());
area.setValue(content);
});
layout.addComponents(area, field, button);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
在代码示例中,我们具有三个组件:TextArea
,TextField
和Button
。
VerticalLayout layout = new VerticalLayout();
在我们的示例中,我们在VerticalLayout
列中显示三个组件。
TextArea area = new TextArea();
area.setWidth("650");
area.setHeight("350");
创建了TextArea
组件; 它的高度增加了。
TextField field = new TextField("Enter an URL");
创建了TextField
组件; 在这里,我们提供要读取的网页的名称。
Button button = new Button("Submit");
button.addClickListener(e -> {
String content = ReadPage.read(field.getValue());
area.setValue(content);
});
在按钮事件处理器中,我们使用ReadPage.read()
方法读取网页,并使用setValue()
方法将 HTML 数据设置为TextArea
。
layout.addComponents(area, field, button);
使用addComponents()
将这三个组件添加到垂直布局中。
ReadPage.java
package com.zetcode.service;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jsoup.Jsoup;
public class ReadPage {
public static String read(String webPage) {
String html = "";
try {
html = Jsoup.connect(webPage).get().html();
} catch (IOException ex) {
Logger.getLogger(ReadPage.class.getName()).log(
Level.WARNING, null, ex);
}
return html;
}
}
read()
方法使用 Jsoup 读取给定网页的内容。
图:Vaadin TextArea
在本教程中,我们展示了 Vaadin TextArea 组件的基础知识。 您可能也对相关教程感兴趣: Jsoup 教程, Vaadin ComboBox
教程, Vaadin Grid
教程, Vaadin Button
教程, Vaadin 滑块教程, Vaadin CheckBox
教程, Java 教程。
Vaadin ComboBox
教程
在 Vaadin ComboBox
教程中,我们学习 Vaadin ComboBox
组件的基础知识。 Vaadin ComboBox
示例在标签组件中显示选定的值。
Vaadin
Vaadin 是一种流行的 Java Web 开发框架,用于构建单页 Web 应用。
Vaadin ComboBox
ComboBox
是允许从下拉列表中选择项目的选择组件。 在可编辑模式下,可以输入新值。
Vaadin ComboBox
示例
以下程序演示了 Vaadin ComboBox
组件的用法。 从ComboBox
中选择的值显示在标签组件中。
NetBeans IDE 可用于轻松创建 Vaadin Web 应用。 我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。
MyUI.java
package com.zetcode.ui;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.HasValue;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
@Theme("mytheme")
@Title("Vaadin ComboBox")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
HorizontalLayout layout = new HorizontalLayout();
Label lbl = new Label();
ComboBox<String> cb = new ComboBox<>();
cb.setTextInputAllowed(false);
cb.setItems("Ubuntu", "Debian", "Arch", "Mint");
cb.setEmptySelectionAllowed(false);
cb.addValueChangeListener((HasValue.ValueChangeEvent<String> event) -> {
String item = event.getValue();
lbl.setValue(item);
});
layout.addComponents(cb, lbl);
layout.setComponentAlignment(lbl, Alignment.MIDDLE_CENTER);
layout.setMargin(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
这是MyUI
类。
HorizontalLayout layout = new HorizontalLayout();
在我们的示例中,我们使用HorizontalLayout
连续显示两个组件。
Label lbl = new Label("");
Label
组件不显示从组合框中选择的值。
ComboBox<String> cb = new ComboBox<>();
创建一个ComboBox
组件。
cb.setTextInputAllowed(false);
使用setTextInputAllowed()
方法,可以禁用ComboBox
的可编辑模式。
cb.setItems("Ubuntu", "Debian", "Arch", "Mint");
使用setItems()
,我们将四个字符串元素添加到ComboBox
中。
cb.setEmptySelectionAllowed(false);
默认情况下,ComboBox
显示一个空项目,允许用户不选择任何项目。 我们使用setEmptySelectionAllowed()
禁用此选项。
cb.addValueChangeListener((HasValue.ValueChangeEvent<String> event) -> {
String item = event.getValue();
lbl.setValue(item);
});
使用addValueChangeListener()
,我们为ComboBox
中的值更改添加了一个监听器。 我们用getValue()
方法获得ComboBox
当前选择的项目。 使用setValue()
方法将检索到的值设置为标签。
layout.addComponents(cb, lbl);
通过addComponents()
方法将这两个组件添加到HorizontalLayout
。
layout.setComponentAlignment(lbl, Alignment.MIDDLE_CENTER);
我们将标签放置在setComponentAlignment()
和Alignment.MIDDLE_CENTER
对齐的行中间。
layout.setMargin(true);
我们在HorizontalLayout
周围留了一些余量。
mytheme.scss
@import "../valo/valo.scss";
@mixin mytheme {
@include valo;
.v-horizontal > .v-spacing {
width: 2em;
}
}
我们增加了HorizontalLayout
中组件之间的间距。
图:Vaadin ComboBox
Vaadin ComboBox
示例 II
在第二个示例中,ComboBox
填充了从服务方法中检索到的城市对象的列表。 使用setItemCaptionGenerator()
产生ComboBox
中显示的字符串。
City.java
package com.zetcode.bean;
import java.io.Serializable;
public class City implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private int population;
public City() {
}
public City(Long id, String name, int population) {
this.id = id;
this.name = name;
this.population = population;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
@Override
public String toString() {
return "City{" + "id=" + id + ", name=" + name +
", population=" + population + '}';
}
}
这是City
bean; 它包含id
,name
和population
属性。
ICityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.List;
public interface ICityService {
public List<City> findAll();
}
ICityService
定义findAll()
合约方法。
CityService.java
package com.zetcode.service;
import com.zetcode.bean.City;
import java.util.ArrayList;
import java.util.List;
public class CityService implements ICityService {
@Override
public List<City> findAll() {
List<City> cities = new ArrayList<>();
cities.add(new City(1L, "Bratislava", 432000));
cities.add(new City(2L, "Budapest", 1759000));
cities.add(new City(3L, "Prague", 1280000));
cities.add(new City(4L, "Warsaw", 1748000));
cities.add(new City(5L, "Los Angeles", 3971000));
cities.add(new City(6L, "New York", 8550000));
cities.add(new City(7L, "Edinburgh", 464000));
cities.add(new City(8L, "Berlin", 3671000));
return cities;
}
}
CityService
返回City
对象的列表。
MyUI.java
package com.zetcode.ui;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.HasValue;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.zetcode.bean.City;
import com.zetcode.service.CityService;
@Theme("mytheme")
@Title("Vaadin ComboBox")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
CityService cityService = new CityService();
HorizontalLayout layout = new HorizontalLayout();
Label lbl = new Label();
ComboBox<City> cb = new ComboBox<>();
cb.setTextInputAllowed(false);
cb.setItems(cityService.findAll());
cb.setEmptySelectionAllowed(false);
cb.setItemCaptionGenerator(City::getName);
cb.addValueChangeListener((HasValue.ValueChangeEvent<City> event) -> {
City city = event.getValue();
String item = String.format("%s: %s", city.getName(),
city.getPopulation());
lbl.setValue(item);
});
layout.addComponents(cb, lbl);
layout.setComponentAlignment(lbl, Alignment.MIDDLE_CENTER);
layout.setMargin(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
这是MyUI
类。
ComboBox<City> cb = new ComboBox<>();
创建参数化的ComboBox
。
cb.setItems(cityService.findAll());
ComboBox
中填充了服务方法中的数据。
cb.setItemCaptionGenerator(City::getName);
使用setItemCaptionGenerator()
,我们选择要在ComboBox
中显示为项目标签的城市名称。
cb.addValueChangeListener((HasValue.ValueChangeEvent<City> event) -> {
City city = event.getValue();
String item = String.format("%s: %s", city.getName(),
city.getPopulation());
lbl.setValue(item);
});
在标签中,我们显示城市名称及其人口,并用冒号分隔。
在本教程中,我们展示了 Vaadin ComboBox
组件的基础知识。 您可能也对相关教程感兴趣: Vaadin 网格教程, Vaadin Button
教程, Vaadin 滑块教程, Vaadin CheckBox
教程, Java 教程。
Vaadin Slider
教程
Vaadin Slider
教程显示了如何使用 Vaadin Slider
组件。 Vaadin 滑块示例在标签组件中显示选定的值。
Vaadin
Vaadin 是流行的 Java Web 开发框架。 Vaadin 用于构建单页 Web 应用。
Vaadin Slider
Slider
是垂直或水平条,允许通过使用鼠标拖动条手柄在定义的范围内选择数值。 在拖动过程中,将显示该值。 Slider
的默认方向为水平,默认范围为0..100
。
Vaadin Slider
示例
以下程序演示了 Vaadin 滑块组件的用法。 从Slider
中选择的值显示在标签组件中。
NetBeans IDE 可用于轻松创建 Vaadin Web 应用。 我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。 我们将使用MyUI.java
文件。
MyUI.java
package com.zetcode.vaadinslider;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.HasValue;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Label;
import com.vaadin.ui.Slider;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
@Theme("mytheme")
@Title("Vaadin Slider")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout verLayout = new VerticalLayout();
Label lbl = new Label("");
Slider slider = new Slider();
slider.addValueChangeListener((HasValue.ValueChangeEvent<Double> event) -> {
Double val = event.getValue();
lbl.setValue(String.valueOf(val));
});
verLayout.addComponents(slider, lbl);
verLayout.setSpacing(true);
setContent(verLayout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
MyUI
是应用入口点。 在这里,我们创建Slider
组件。
Label lbl = new Label("");
这是Label
组件,它不使用从滑块中选择的值。
Slider slider = new Slider();
在默认的水平方向上会创建一个Slider
组件。
slider.addValueChangeListener((HasValue.ValueChangeEvent<Double> event) -> {
Double val = event.getValue();
lbl.setValue(String.valueOf(val));
});
使用addValueChangeListener()
,我们为值更改添加了一个监听器。 我们用getValue()
方法获得Slider
的当前值。 检索到的值通过setValue()
方法设置为标签。
verLayout.addComponents(slider, lbl);
verLayout.setSpacing(true);
我们使用addComponents()
方法将标签和滑块添加到布局管理器中。 组件之间的一些间距通过setSpacing()
添加。
mytheme.scss
@import "../valo/valo.scss";
@mixin mytheme {
@include valo;
.v-slider { width: 20em }
}
我们增加mytheme.scss
文件中Slider
的宽度。
图:Vaadin Slider
在本教程中,我们展示了如何使用 Vaadin Slider
组件。 您可能也对相关教程感兴趣: Vaadin ComboBox
教程, Vaadin Button
教程, Vaadin TextArea
教程, Vaadin CheckBox
教程, Java 教程。
Vaadin CheckBox
教程
Vaadin CheckBox
教程显示了如何使用 Vaadin CheckBox
组件。 Vaadin CheckBox
示例切换网页的标题。
Vaadin
Vaadin 是流行的 Java Web 框架。 它用于构建单页 Web 应用。
Vaadin CheckBox
CheckBox
是可以选中或取消选中的选择组件。 单击复选框将更改其状态。 可以使用setValue()
方法检查CheckBox
。 CheckBox
的值通过getValue()
方法检索。 更改复选框的值将导致ValueChangeEvent
,由ValueChangeListener
处理。
Vaadin CheckBox
示例
以下程序演示了 Vaadin CheckBox
组件的用法。 复选框可切换网页标题。
Vaadin Web 应用可以在 NetBeans 中轻松创建。 我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。 我们将使用MyUI.java
文件。
MyUI.java
package com.zetcode.vaadincheckboxex;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.Page;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
CheckBox cbox = new CheckBox("Show title");
cbox.setValue(true);
Page.getCurrent().setTitle("CheckBox");
cbox.addValueChangeListener(e -> {
Boolean val = e.getValue();
if (val) {
Page.getCurrent().setTitle("CheckBox");
} else {
Page.getCurrent().setTitle(".");
}
});
layout.addComponents(cbox);
layout.setMargin(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
MyUI
是应用入口点。 在这里,我们创建CheckBox
组件。
CheckBox cbox = new CheckBox("Show title");
cbox.setValue(true);
创建一个新的CheckBox
。 构造器的参数是CheckBox
的标签。 由于默认情况下网页的标题是可见的,因此我们使用setValue()
方法检查组件。
Page.getCurrent().setTitle("CheckBox");
在这里,我们设置网页的标题。 我们也可以使用@Title
注解设置页面标题。
cbox.addValueChangeListener(e -> {
Boolean val = e.getValue();
if (val) {
Page.getCurrent().setTitle("CheckBox");
} else {
Page.getCurrent().setTitle(".");
}
});
使用addValueChangeListener()
,我们为值更改添加了一个监听器。 我们用getValue()
方法获得CheckBox
的当前值。 根据其状态,设置网页的标题。
layout.addComponents(cbox);
通过addComponents()
方法将CheckBox
添加到布局中。
layout.setMargin(true);
使用setMargin()
方法,我们在CheckBox
组件周围创建了一些边距。
图:Vaadin CheckBox
在本教程中,我们展示了如何使用 Vaadin CheckBox
组件。 您可能也对相关教程感兴趣: Vaadin Button
教程, Vaadin ComboBox
教程, Vaadin TextArea
教程, Vaadin 滑块教程, Java 教程。
Vaadin Button
教程
Vaadin Button
教程显示了如何使用 Vaadin Button
组件。 Vaadin 按钮示例显示一个小的通知窗口。
Vaadin
Vaadin 是用于构建单页 Web 应用的流行 Java 框架。
Vaadin Button
Button
用于触发某些动作。 当用户单击按钮时,会触发Button.ClickEvent
。 通过onClick()
或addClickListener()
方法添加了按钮事件处理器。
Vaadin Button
示例
以下程序演示了 Vaadin Button
组件的用法。 当我们单击按钮组件时,将显示一个通知窗口。
$ tree
.
├── nb-configuration.xml
├── pom.xml
├── README.md
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── zetcode
│ │ └── vaadinbuttonex
│ │ └── MyUI.java
│ └── webapp
│ ├── META-INF
│ │ └── context.xml
│ ├── VAADIN
│ │ └── themes
│ │ └── mytheme
│ │ ├── addons.scss
│ │ ├── favicon.ico
│ │ ├── mytheme.scss
│ │ └── styles.scss
│ └── WEB-INF
└── test
└── java
这是 Vaadin Web 应用的项目结构。 该项目是在 NetBeans 中创建的。 在 NetBeans 中,我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。 我们将使用MyUI.java
文件。
MyUI.java
package com.zetcode.vaadinbuttonex;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
@Theme("mytheme")
@Title("Vaadin Button")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
HorizontalLayout layout = new HorizontalLayout();
Button button = new Button("Button");
button.addClickListener(e -> {
Notification.show("Button clicked");
});
layout.addComponents(button);
layout.setMargin(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
MyUI
是应用入口点。 UI 可以表示浏览器窗口(或选项卡),也可以表示嵌入 Vaadin 应用的 HTML 页面的某些部分。
@Theme("mytheme")
使用@Theme
批注,我们指定应用的主题。 系统会自动为我们创建一个默认的mytheme
。
@Title("Vaadin Button")
使用@Title
批注,设置浏览器窗口的标题。
HorizontalLayout layout = new HorizontalLayout();
Vaadin 中的组件放置在布局管理器中。 HorizontalLayout
将组件连续放置。 由于我们只有一个Button
组件,因此选择哪个布局管理器都没关系。
Button button = new Button("Button");
创建了一个新的Button
组件。 将按钮的标签指定为参数。
button.addClickListener(e -> {
Notification.show("Button clicked");
});
addClickListener()
方法添加了按钮事件处理器。 在方法的主体中,我们称为Notification.show()
,它在页面中间显示一个小的通知窗口。
layout.addComponents(button);
使用addComponents()
方法将按钮添加到布局。
layout.setMargin(true);
使用setMargin()
方法,我们在按钮组件周围创建了一些边距。
setContent(layout);
最后,将布局添加到 UI。
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
从此代码摘录中可以看到,Vaadin 框架是基于 Java Servlet 技术构建的。
在本教程中,我们展示了如何使用 Vaadin Button
组件。 您可能也对相关教程感兴趣: Vaadin CheckBox
教程, Vaadin DateField
教程, Vaadin ComboBox
教程, Vaadin TextArea
教程, Vaadin 滑块教程, Java 教程。
Vaadin DateField
教程
在 Vaadin DateField
教程中,我们学习 Vaadin DateField
组件的基础。 在 Vaadin DateField
示例中,所选日期将转换为回教日期,并显示在Label
组件中。
Vaadin
Vaadin 是一种流行的 Java Web 开发框架,用于构建单页 Web 应用。
Vaadin DateField
DateField
是日期输入组件,它将实际的日期选择器显示为弹出窗口。
Vaadin DateField
示例
以下程序演示了 Vaadin DateField
组件的用法。 所选日期将转换为回教日期,并显示在Label
组件中。
NetBeans IDE 可用于轻松创建 Vaadin Web 应用。 我们需要安装 NetBeans Vaadin 插件,然后创建一个新的 Vaadin Web 应用项目。
MyUI.java
package com.zetcode.main;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.HasValue;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.DateField;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.zetcode.service.DateConvert;
import java.time.LocalDate;
import java.time.chrono.HijrahDate;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
VerticalLayout layout = new VerticalLayout();
DateField field = new DateField();
Label lbl = new Label();
field.addValueChangeListener((HasValue.ValueChangeEvent<LocalDate> event) -> {
LocalDate date = (LocalDate) event.getValue();
HijrahDate hjdate = DateConvert.toIslamic(date);
lbl.setValue(hjdate.toString() + "!");
});
layout.addComponents(field, lbl);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
这是MyUI
类。
VerticalLayout layout = new VerticalLayout();
在我们的示例中,我们在VerticalLayout
列中显示两个组件。
DateField field = new DateField();
创建一个DateField
组件。
Label lbl = new Label("");
Label
组件将支付从日期字段中选择的值。
field.addValueChangeListener((HasValue.ValueChangeEvent<LocalDate> event) -> {
LocalDate date = (LocalDate) event.getValue();
HijrahDate hjdate = DateConvert.toIslamic(date);
lbl.setValue(hjdate.toString() + "!");
});
使用addValueChangeListener()
,我们为DateField
中的值更改添加了一个监听器。 我们用getValue()
方法获得DateField
当前选择的项目。 使用DateConvert.toIslamic()
将检索到的值转换为回教日期,并使用setValue()
方法将其设置为标签。
layout.addComponents(field, lbl);
通过addComponents()
方法将这两个组件添加到VerticalLayout
。
layout.setMargin(true);
我们在VerticalLayout
周围留了一些余量。
DateConvert.java
package com.zetcode.service;
import java.time.LocalDate;
import java.time.chrono.HijrahChronology;
import java.time.chrono.HijrahDate;
public class DateConvert {
public static HijrahDate toIslamic(LocalDate date) {
HijrahDate hjdate = HijrahChronology.INSTANCE.date(date);
return hjdate;
}
}
在DateConvert
类中,我们使用HijrahChronology
计算伊斯兰日期。
图:Vaadin DateField
在本教程中,我们展示了 Vaadin DateField
组件的基础。 您可能也对相关教程感兴趣: Vaadin 网格教程, Vaadin 链接教程, Vaadin Button
教程, Vaadin 滑块教程, Vaadin CheckBox
教程, Java 教程。
Vaadin Link
教程
在 Vaadin Link
教程中,我们学习 Vaadin Link
组件的基础。 在 Vaadin 链接示例中,我们创建一个链接组件,该组件会将我们转移到外部网页。
Vaadin
Vaadin 是流行的 Java Web 开发框架。 它用于构建单页 Web 应用。
Vaadin Link
Link
组件允许建立超链接。 这是浏览器本地处理的常规 HTML 超链接(<a href>
)。 与单击Button
时不同,单击Link
不会在服务器端引起事件。 可以使用ExternalResource
链接到任意 URL。
Vaadin Link
示例
以下程序演示了 Vaadin Link
组件的用法。 该链接包含文本和图像。
我们可以使用setTargetName("_blank")
在新窗口中打开页面。
MyUI.java
package com.zetcode;
import javax.servlet.annotation.WebServlet;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.FileResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Link;
import com.vaadin.ui.UI;
import java.io.File;
@Theme("mytheme")
public class MyUI extends UI {
@Override
protected void init(VaadinRequest vaadinRequest) {
HorizontalLayout layout = new HorizontalLayout();
Link link = new Link("Go to stackoverflow.com",
new ExternalResource("https://stackoverflow.com/"));
String basepath = VaadinService.getCurrent()
.getBaseDirectory().getAbsolutePath();
FileResource resource = new FileResource(new File(basepath
+ "/WEB-Iimg/web.png"));
link.setIcon(resource);
layout.setMargin(true);
layout.addComponents(link);
setContent(layout);
}
@WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
@VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
public static class MyUIServlet extends VaadinServlet {
}
}
该示例创建一个链接组件,该组件将重定向到外部网页。
Link link = new Link("Go to stackoverflow.com",
new ExternalResource("https://stackoverflow.com/"));
创建了Link
组件; 该网页由ExternalResource
指定。
String basepath = VaadinService.getCurrent()
.getBaseDirectory().getAbsolutePath();
使用getBaseDirectory()
,我们检索应用上下文目录。 需要获取图标图像的路径。
FileResource resource = new FileResource(new File(basepath
+ "/WEB-Iimg/web.png"));
该图像是FileResource
,位于/WEB-Iimg/
目录中。
图:Vaadin Link
在本教程中,我们展示了 Vaadin Link
组件的基础。 您可能也对相关教程感兴趣: Vaadin 网格教程, Vaadin DateField
教程, Vaadin Button
教程, Vaadin 滑块教程, Vaadin CheckBox
教程, Java 教程。