Seth Kenlon 发布的文章

这个看似简单的编辑器为用户提供了许多易于学习和使用的命令。

 title=

GNU ed 命令是一个行编辑器。它被认为是标准的 Unix 文本编辑器,因为它是首个出现在 Unix 的文本编辑器,并且它曾经无处不在,你在任何一个 POSIX 系统中都能找到它(通常来说,你现在也可以)。在某种程度上,你可以很容易看出来它是第一个文本编辑器,因为它在许多方面的功能都十分基础。和其他大多数的文本编辑器不同,它不会打开一个属于自己的窗口或显示区域,事实上,在默认情况下,它甚至不会提示用户输入文字。从另一个方面来说,它在交互功能上的缺失也可以成为一个优点。它是一个多功能的编辑器,你可以用简短的命令控制它,无论是在交互式的命令行中,还是在编写的 shell 脚本里。

安装 ed

如果你正在使用 Linux 或者 BSD 的话,你很可能已经默认安装了 ed(在 Linux 上是 GNU 版 ed,而在 BSD 上是 BSD 版 ed)。但是,一些极简的环境可能没有包括 ed,这也没关系,你的发行版的软件仓库中很可能有 ed 可供下载。macOS 默认安装了 BSD 版 ed。

启动 ed

当你启动 ed 的时候,你的终端提示符不见了,看起来好像是 ed 停止运行了。其实它没有,它只是在等待你输入指令而已。

$ ed

为使 ed 显示更详细的信息,你可以输入命令 p 让它返回一个提示符:

$ ed
p
?

这个问号(?)是默认的 ed 提示符。

缓冲区

当 ed 激活时,你其实是在和一个叫 缓冲区 buffer 的东西打交道。缓冲区是内存中的一块区域。你并不会直接编辑文件,而是在编辑它对应的缓冲区。当你退出 ed 却没有把修改保存到磁盘的文件上时,所有的修改都会丢失,因为它们只在缓冲区里存在。(这对于一个已经习惯了初始的 草图缓冲区 scratch buffer 的资深 Emacs 用户可能很耳熟。)

使用 ed 输入文本

启动 ed 后,你处于命令模式。这意味着你可以向编辑器发送指令,比如让它显示一个提示符,而不是空白区域。你可以使用 a 命令开始附加文本到当前的缓冲区,使用一个实心的点 . 来终止输入。比如,下面的这个例子往缓冲区里附加了两行文字(“hello world” 和 “hello ed”):

?
a
hello world
hello ed
.

使用点 . 终止输入后,你将回到命令模式。

查看缓冲区

怎样查看当前缓冲区里都有什么呢?你可以输入想要查看的行号,也可以使用 ,p 命令来显示所有的行:

?
1
hello world
2
hello ed
,p
hello world
hello ed

写入文件

如果你现在对文本很满意,你可以使用 w 命令把缓冲区写入到文件中,后面跟上目标文件名:

?
w example.txt
19

写操作后显示的那个数字代表着写入到文件中的字符数。

读取文件

除了使用 ed 来读取文本,你也可以使用 r 命令把一个已经存在的文件加载到到缓冲区里:

?
r myfile.txt

另外,你也可以在启动 ed 时,在它后面加上你想要加载到缓冲区里的文件名:

$ ed myfile.txt

编辑缓冲区

鉴于 ed 是一个文本编辑器,你当然可以使用一种特殊的语法来编辑缓冲区里的文本。使用 sed 或 vim 的用户或许会觉得这个语法很熟悉。假设现在缓冲区里已经加载了一个文件:

$ ed myfile.txt
,p
This is an example document.
There is some text, but not much.
There is some errors, but not much.

如果你要把第一句话中的 document 修改为 file,你可以先选择目标行(1),然后使用 s 命令调用搜索函数,后面跟着搜索文本和替换文本:

?
1
This is an example document.
s/document/file/
1
This is an example file.

如果你要编辑其他行,步骤也是一样的,只需提供一个不同的行号即可:

?
3
There is some errors, but not much.
s/is/are/
s/much/many/

你可以使用 ,p 命令来看到你对缓冲区的历史编辑记录:

This is an example file.
There is some text, but not much.
There are some errors, but not many.

当然,这些修改只存在于缓冲区里。你如果在 ed 编辑器外查看这个文件,你只会看到原始的文本:

$ cat myfile.txt
This is an example document.
There is some text, but not much.
There is some errors, but not much.

如果你要把这些修改保存回文件中,使用 w 命令即可:

w myfile.txt
258

清空缓冲区

如果想要得到一个新的缓冲区,以此来打开一个新的文件,或者把一个新的文件加载到不同的环境中,你可以使用 c 命令。使用这个清空缓冲区后,什么也不会输出,因为缓冲已经是空的了:

c
,p

退出

如果要退出当前的 ed 会话,你可以使用 q 命令。它并不会给你一个保存缓冲区的机会,所以你要确保自己在这之前执行了保存操作。

尝试一下 ed 吧

ed 还可以做到很多事情,学习 ed 可以让你知道它和部分的 vim 是如何工作的。我并没有尝试使用 ed 来写这篇文章,老实说,我也不认为它是通常意义上的最佳文本编辑器。但是,ed 仍然是一个出色的编辑器。通过阅读它的文档,你可以很轻松地学会它。在 GNU 系统上,你可以使用 info ed 来查看它的操作手册。


via: https://opensource.com/article/20/12/gnu-ed

作者:Seth Kenlon 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文介绍了几种端口转发最常见的使用场景。

 title=

端口转发就是把网络流量从一个网络监听者(称为一个“端口”)发送到另一个上,无论这两个端口是否属于同一台电脑。在这里,端口不是某个物理实体,而是一个监听网络活动的软件程序。

当流量被定向发往到某个特定的端口,它会先到达一个路由器或是防火墙,亦或是其他的网络程序。它最终收到的响应可能会根据它想要通讯的端口来定义。比如,当你使用端口转发时,你可以捕获到发往 8080 端口的流量,然后把它转发到 80 端口。对于接收信号的原端口来说,这个新的目标端口可能和它在同一台设备上,也可能是在另一台设备上。我们在很多情况下都会用到端口转发,实现的方式也有很多。本文将介绍其中最常见的几种使用场景。

使用路由器来进行端口转发

如果你在把服务器架设在家里,那么你通常是不需要转发端口的。你的家庭路由器(通常是你从 网络服务提供商 Internet Service Provider (ISP)获得的 WiFi 设备)有一个内置的防火墙,它的作用是阻止外面的世界访问到你的家庭网络。通过使用端口转发,你可以允许某个指定端口的流量穿过路由器的防火墙,并发送到局域网中的某个指定的 IP 地址。

比如说,你架设了一个 Minetest 服务,并想要邀请你的朋友们来试试。为了让他们能够“穿过”你的路由器,从而到达这个 Minetest 服务,你必须把路由器上的某个端口转发到托管 Minetest 服务的电脑上。Minetest 服务默认运行在 30000 端口。你可以把路由器的 30000 端口转发到你的电脑的 30000 端口上,或者你也可以随便转发到一个更简单的端口上,这样玩家们会更容易记住它。我发现,当使用 30000 端口的时候,人们时常会少数几个 0(特别是没有逗号分隔符的帮助时),所以我一般使用路由器的 1234 端口,然后把它转发到我内部的 30000 端口。

每个制造商的路由器接口都不一样,但是不管你用的是什么牌子的路由器,方法都是相同的。首先,你需要登录到你的路由器。

通常,路由器的 IP 地址和登录信息都会打印在路由器上,或者在是它的文档里。我有一个型号为 TP-Link GX90 的路由器,我在浏览器里访问 10.0.1.1 就可以登录它,但你的路由器可能是 192.168.0.1 或者其他的地址。

我的 GX90 路由器把端口转发功能称为“ 虚拟服务器 virtual servers ”,它是路由器的“NAT 转发”标签下的一个功能选项。NAT 的意思是 “ 网络地址转换 Network Address Translation ”。在其他路由器中,这个功能可能直接就叫做“端口转发”,或者叫“防火墙”、“服务”等。找到正确的功能选项可能需要花费一些时间,因此,你可能需要花点时间研究下你的路由器文档。

当你找到了路由器的端口转发设置,添加一个新规则,命名一个外部端口(在我的例子中是 1234)和一个内部端口(30000)。把外部端口转发到内部端口上,而内部端口绑定在你想要大家访问的电脑的 IP 地址上。如果你需要一些查询本机 IP 地址的帮助,你可以阅读 Archit Modi 写的 《在 Linux 上如何查询本地 IP 地址》。

 title=

一个简单端口转发规则

(图片提供者是 Seth Kenlon,遵循 署名-相同方式共享 4.0 国际 协议)

在这个例子中,访问家庭网络的 1234 端口的流量,都会被转发到了我的家庭服务器的 30000 端口上,后者的 IP 地址是 10.0.1.2。

在继续之前,先保存这个规则。

接下来,你需要知道你的家庭网络的公网 IP 地址是多少。你可以从 ifconfig.me 或者 icanhazip.com 上获得这个地址。你可以在浏览器中打开这两个网站的其中一个,也可以使用 curl 命令来获取到这个 IP。

$ curl ifconfig.me
93.184.216.34

现在,你的朋友们就可以在 Minetest 客户端里输入 169.169.23.49:1234,加入你的 Minetest 服务器啦。

使用防火墙来进行端口转发

系统管理员有时候需要转发访问服务器的流量。比如说,你可能想要接收来自 80 端口的流量,但是用户的服务却运行在 8065 端口。如果不进行端口转发的话,你的用户就不得不在输入浏览器的 URL 末尾,加上一个指定的端口号,例如 example.com:8065。大多数用户都不习惯于考虑端口的问题,所以你需要把访问网络通用的 80 端口的请求拦截下来,然后转发到你的网络应用的具体端口,这会给用户带来巨大的方便。

你可以在服务器上使用 firewall-cmd 来转发流量,它是访问 firewalld 后台进程的 前端 front-end 命令。

首先,设置好你想要转发的端口和协议:

$ sudo firewall-cmd \
    --add-forward-port \
    port=80:proto=tcp:toport=8065

为使修改永久生效,你需要加上 --runtime-to-permanent 选项:

$ sudo firewall-cmd --runtime-to-permanent

网络转发

在网络传输中,除了端口转发外,还有其他种类的 转发 forwarding 形式,例如 IP 转发和代理等。当你熟悉了网络信息在路由时是怎么被处理的之后,你可以试试不同的转发形式(然后使用 tcpdump 或类似的工具)来看看哪一种最好、最符合你的需求。


via: https://opensource.com/article/21/9/what-port-forwarding

作者:Seth Kenlon 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

使用这个 Linux KDE 应用,将你的档案数字化。

 title=

虽然现在的世界已经大部分实现了数字化,但仍有一些时候,你还是需要打印一份表格,签字,然后把它扫描回来。有时候,我发现在手机上拍个快照就够了,但有些行业需要比草率的快照更好的复印件,因此平板扫描仪是必要的。KDE 项目提供了一个叫做 Skanlite 的应用程序,它可以帮助你导入在平板上扫描的文件,甚至是用联机相机扫描的文件。

在 Linux 上安装 Skanlite

你可以从你的软件库中安装 Skanlite。在 Fedora、Mageia 和类似的地方:

$ sudo dnf install skanlite

在 Elementary、Linux Mint 和其他基于 Debian 的发行版上:

$ sudo apt install skanlite

扫描仪驱动

市场上的大多数扫描仪都与 SANE 扫描仪 API 兼容。SANE 并不是一个真正的驱动,而是一个协议,可以接收来自图像采集设备的输入,并为希望围绕它建立一个应用的程序员提供选项。Skanlite 就是这样一个应用。

我还没有遇到过不与 SANE 接口兼容的扫描仪,但可能也有不与 SANE 接口兼容的扫描仪。在这些情况下,请在制造商的网站上寻找 SANE 或 TWAIN 的驱动,或者寻找它们专有的驱动和扫描仪接口。后者可能无法使用 Skanlite,但当你不确定你的扫描仪是否通过标准协议进行通信时,Skanlite 总是值得试试。我甚至遇到过打印机和扫描仪的二合一设备,尽管打印机需要一个额外的驱动,但扫描仪却能立即被识别。

使用 Skanlite

当你启动 Skanlite 时,它首先会在你的系统中搜索图像采集设备。在笔记本电脑上,Skanlite 通常会发现网络摄像头是一个有效的输入源(因为它是),但它也会找到连接到你机器上的平板扫描仪。选择你要使用的扫描仪,然后继续。

要看扫描的内容,点击应用程序右下角的“ 预览 Preview ”按钮。

 title=

这将在右面板上显示一个预览图像。没有任何东西被保存到你的硬盘上,这只是显示你的扫描仪上目前有什么。

选择一个扫描区域

如果你只需要扫描仪上的一部分内容,你可以选择一个你想保存的区域。要选择一个单一的区域,在你想保存的区域上点击并拖动你的鼠标。当有一个有效的选择时,当你点击“ 扫描 Scan ”按钮时,只有你选择的那部分会被保存。

你可以有一个以上的选区,当你需要扫描几个小图像或只扫描一个大文件的特定部分时,这特别有效。要添加一个选区,请点击出现在选区中心的 “+” 图标。

 title=

你可以通过点击 “-” 图标来删除选区,当你有多个活动选区时,该图标会出现。

扫描设置

图像采集设置位于左边的面板上。这些控件允许你导入彩色或灰度的图像,并对图像的亮度和对比度进行调整。这些选项是基于软件的,不影响你的扫描仪的行为方式,但它们是常见的调整,在这里做这些调整可以使你不必在 GIM 或 Gwenview 中对图像进行后期处理。

在许多情况下,你的扫描仪可能有可配置的设置,可在 Skanlite 窗口左侧的“ 扫描仪特定选项 Scanner Specific Options ”标签中找到。有些扫描仪允许你调整色温、亮度、饱和度和其他出现在固件中的属性。可用的选项根据设备和供应商的不同而不同,所以你有可能在这个面板上看到变化,这取决于你与哪种设备的对接。

扫描和保存

当你准备好导入图像(或图像的选定区域,如果你已经做了选择)时,点击 Skanlite 窗口右下角的“ 扫描 Scan ”按钮。根据你的设备,它可能需要一些时间来创建扫描,但当它完成后,会提示你保存或丢弃图像。如果你喜欢你所看到的,点击“ 保存 Save ”。

图像会被保存到你所配置的任何默认位置。要查看默认位置,点击窗口右下角的“ 设置 Settings ”按钮。在 “ Skanlite 设置 Skanlite Settings ”中,你可以设置默认保存位置、默认名称格式和图像分辨率。你还可以控制每次扫描后是否提示你保存或丢弃图像,或者你是否想要保存所有的东西并在以后进行分类。

Linux 上的扫描很容易

在 Linux 上扫描文件是如此简单,我很少考虑这个问题。通常不需要你去寻找和安装特殊的驱动或应用,因为像 Skanlite 这样的应用使用开放协议,使这个过程变得简单。下次你有一份需要数字化的拷贝时,用 Skanlite 导入它。


via: https://opensource.com/article/22/2/scan-documents-skanlite-linux-kde

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们收集了一些最受欢迎的简明指南,它们既能满足你充分利用暑假的愿望,又能满足你为下一个学期做规划的需要。

对一些老师来说,夏天到了,一个漫长的(希望也是放松的)假期也到了。所有我认识的老师都是自豪的终身学习者,尽管暑假过后,又有一个新学期会到来。为了帮助你充分利用暑假时间,与此同时也为即将到来的下一个学期做好准备,我们收集了一些最受欢迎的 简明 指南。

如何让你的学校做好准备(在新冠疫情下)

通过 在 Linux 上来完成所有相关工作,Robert Maynord 老师确保了他的学校为远程学习做好了准备,甚至在疫情前他就这么做了。虽然我们还不知道在今年剩下的时间里会发生什么,但是,如果说新冠疫情向世界展示了什么,那就是 数字转型(指把数字技术融入到教育的各个领域)不仅是可能的,而且对教师和学生来说都是有益的。你可能无权在技术层面上改变课堂的运作方式,但你仍然可以做很多小的改变,为学生创造更灵活的学习体验。

为教师准备的终极开源指南

通过本文,你可以学习如何在课堂上 融入开源原则。开源不仅仅和科技相关,它同时也关于知识共享、团队协作以及为了一个共同目标而努力。你可以把你的教室变成一个共享的空间,让学生们互相学习,就像他们向你学习一样。阅读开源,把开源付诸实践,并鼓励学生们积极参与。

8 个为虚拟教室准备的 WordPress 插件

WordPress Web 平台是一个构建网站的强大工具。在教室里,它可以作为教授 Web 技术、创意写作和学术写作的 一个很好的工具。它也可以被用来帮助远程学习,或者是把日常的学校作业数字化。通过掌握 WordPress 的诸多 [附加功能],你可以从中获取到最大的教育收益。

教孩子们写 Python(交互式游戏)

开源工具可以帮助任何人以一种轻松有趣的方式开始学习 Python —— 那就是制作游戏。当然,Python 涉及到很多方面的东西。别担心,我们有一个课程可以带你从安装 Python 开始,通过简单的文本代码和 “ 海龟 turtle ” 绘图游戏开始你的第一步,一直到中级游戏开发。

  1. 首先,安装 Python,阅读我们的 Python 入门文章,熟悉编程的概念。单单是这篇文章里的内容就可以作为两节或三节不同课程的基础哦。
  2. 然后,如果你熟悉 Jupyter 库的话,可以学习 使用 Python 和 Jupyter 来编写一个简单游戏。
  3. 接着,你也可以 在这本 Python 电子书里学到游戏开发的知识,里面会教你如何使用 Git、Python 和 PyGame 库。当你学会了这些基础内容,你可以看看 这本书里的 "游戏测试员" 的有趣创作集合。

如果 Python 对你或你的学生来说太难了,那么看看 Thine 吧,它是一个简单的基于 HTML 的交互式的讲故事工具。

教孩子们玩树莓派(编程)

我们的指南中有一篇 树莓派入门指南,其中探索了各种帮助孩子们学习编程的资源。树莓派的特点是它足够便宜,只要花 35 美元,你就可以买到一个全功能的 Linux 电脑。然后你就在上面做任何事,不管是基本的 Python 学习还是搭建实际的网络服务器,因此,它有着巨大的教育潜力。你完全可以为每一个学生都配一个树莓派,或者你也可以让班里的学生共享一个树莓派(Linux 是多用户操作系统,只要设置得当,所有的学生都可以同时使用这个树莓派,直到你说服他们的家长购买更多树莓派)。

一起学习

开放课堂的关键之一是要勇敢地和学生一起学习。作为一个老师,你可能习惯了掌握所有的答案,但是数字世界是不断改变和进化的。不要害怕 和 你的学生们一起学习 Python、Linux、树莓派或者任何其他东西,一起学习新的基础知识、小技巧和解决问题的新方式。开源是一种经过验证的成功方法,所以不要只是教授开源而已,还要让开源在你的课堂上得以运用。


via: https://opensource.com/article/21/6/open-source-guides-teachers

作者:Seth Kenlon 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在你使用 Java 编写软件时实现持久化配置。

 title=

当你编写一个应用时,你通常都会希望用户能够定制化他们和应用交互的方式,以及应用与系统进行交互的方式。这种方式通常被称为 “ 偏好 preference ” 或者 “ 设置 setting ”,它们被保存在一个 “偏好文件” 或者 “配置文件” 中,有时也直接简称为 “ 配置 config ”。配置文件可以有很多种格式,包括 INI、JSON、YAML 和 XML。每一种编程语言解析这些格式的方式都不同。本文主要讨论,当你在使用 Java 编程语言 来编写软件时,实现持久化配置的方式。

选择一个格式

编写配置文件是一件相当复杂的事情。我曾经试过把配置项使用逗号分隔保存在一个文本文件里,也试过把配置项保存在非常详细的 YAML 和 XML 中。对于配置文件来说,最重要是要有一致性和规律性,它们使你可以简单快速地编写代码,从配置文件中解析出数据;同时,当用户决定要做出修改时,很方便地保存和更新配置。

目前有 几种流行的配置文件格式。对于大多数常见的配置文件格式,Java 都有对应的 库 library 。在本文中,我将使用 XML 格式。对于一些项目,你可能会选择使用 XML,因为它的一个突出特点是能够为包含的数据提供大量相关的元数据,而在另外一些项目中,你可能会因为 XML 的冗长而不选择它。在 Java 中使用 XML 是非常容易的,因为它默认包含了许多健壮的 XML 库。

XML 基础

讨论 XML 可是一个大话题。我有一本关于 XML 的书,它有超过 700 页的内容。幸运的是,使用 XML 并不需要非常了解它的诸多特性。就像 HTML 一样,XML 是一个带有开始和结束标记的分层标记语言,每一个标记(标签)内可以包含零个或更多数据。下面是一个 XML 的简单示例片段:

<xml>
  <node>
    <element>Penguin</element>
  </node>
</xml>

在这个 自我描述的 self-descriptive 例子中,XML 解析器使用了以下几个概念:

  • 文档 Document :<xml> 标签标志着一个 文档 的开始,</xml> 标签标志着这个文档的结束。
  • 节点 Node :<node> 标签代表了一个 节点。
  • 元素 Element :<element>Penguin</element> 中,从开头的 < 到最后的 > 表示了一个 元素。
  • 内容 Content : 在 <element> 元素里,字符串 Penguin 就是 内容。

不管你信不信,只要了解了以上几个概念,你就可以开始编写、解析 XML 文件了。

创建一个示例配置文件

要学习如何解析 XML 文件,只需要一个极简的示例文件就够了。假设现在有一个配置文件,里面保存的是关于一个图形界面窗口的属性:

<xml>
  <window>
    <theme>Dark</theme>
    <fullscreen>0</fullscreen>
    <icons>Tango</icons>
</window>
</xml>

创建一个名为 ~/.config/DemoXMLParser 的目录:

$ mkdir ~/.config/DemoXMLParser

在 Linux 中,~/.config 目录是存放配置文件的默认位置,这是在 自由桌面工作组 的规范中定义的。如果你正在使用一个不遵守 自由桌面工作组 Freedesktop 标准的操作系统,你也仍然可以使用这个目录,只不过你需要自己创建这些目录了。

复制 XML 的示例配置文件,粘贴并保存为 ~/.config/DemoXMLParser/myconfig.xml 文件。

使用 Java 解析 XML

如果你是 Java 的初学者,你可以先阅读我写的 面向 Java 入门开发者的 7 个小技巧。一旦你对 Java 比较熟悉了,打开你最喜爱的集成开发工具(IDE),创建一个新工程。我会把我的新工程命名为 myConfigParser。

刚开始先不要太关注依赖导入和异常捕获这些,你可以先尝试用 javax 和 java.io 包里的标准 Java 扩展来实例化一个解析器。如果你使用了 IDE,它会提示你导入合适的依赖。如果没有,你也可以在文章稍后的部分找到完整的代码,里面就有完整的依赖列表。

Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");
File configFile = new File(configPath.toString(), "myconfig.xml");

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

DocumentBuilder builder = null;
builder = factory.newDocumentBuilder();

Document doc = null;
doc = builder.parse(configFile);
doc.getDocumentElement().normalize();

这段示例代码使用了 java.nio.Paths 类来找到用户的主目录,然后在拼接上默认配置文件的路径。接着,它用 java.io.File 类来把配置文件定义为一个 File 对象。

紧接着,它使用了 javax.xml.parsers.DocumentBuilder 和 javax.xml.parsers.DocumentBuilderFactory 这两个类来创建一个内部的文档构造器,这样 Java 程序就可以导入并解析 XML 数据了。

最后,Java 创建一个叫 doc 的文档对象,并且把 configFile 文件加载到这个对象里。通过使用 org.w3c.dom 包,它读取并规范化了 XML 数据。

基本上就是这样啦。理论上来讲,你已经完成了数据解析的工作。可是,如果你不能够访问数据的话,数据解析也没有多少用处嘛。所以,就让我们再来写一些查询,从你的配置中读取重要的属性值吧。

使用 Java 访问 XML 的值

从你已经读取的 XML 文档中获取数据,其实就是要先找到一个特定的节点,然后遍历它包含的所有元素。通常我们会使用多个循环语句来遍历节点中的元素,但是为了保持代码可读性,我会尽可能少地使用循环语句:

NodeList nodes = doc.getElementsByTagName("window");

for (int i = 0; i < nodes.getLength(); i++) {
 Node mynode = nodes.item(i);
 System.out.println("Property = " + mynode.getNodeName());
       
 if (mynode.getNodeType() == Node.ELEMENT_NODE) {
   Element myelement = (Element) mynode;
             
   System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());
   System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());
   System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent());
 }
}

这段示例代码使用了 org.w3c.dom.NodeList 类,创建了一个名为 nodes 的 NodeList 对象。这个对象包含了所有名字匹配字符串 window 的子节点,实际上这样的节点只有一个,因为本文的示例配置文件中只配置了一个。

紧接着,它使用了一个 for 循环来遍历 nodes 列表。具体过程是:根据节点出现的顺序逐个取出,然后交给一个 if-then 子句处理。这个 if-then 子句创建了一个名为 myelement 的 Element 对象,其中包含了当前节点下的所有元素。你可以使用例如 getChildNodes 和 getElementById 方法来查询这些元素,项目中还 记录了 其他查询方法。

在这个示例中,每个元素就是配置的键。而配置的值储存在元素的内容中,你可以使用 .getTextContent 方法来提取出配置的值。

在你的 IDE 中运行代码(或者运行编译后的二进制文件):

$ java ./DemoXMLParser.java
Property = window
Theme = Dark
Fullscreen = 0
Icon set = Tango

下面是完整的代码示例:

package myConfigParser;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ConfigParser {

        public static void main(String[] args) {
                Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");
                File configFile = new File(configPath.toString(), "myconfig.xml");
                DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = null;
               
                try {
                        builder = factory.newDocumentBuilder();
                } catch (ParserConfigurationException e) {
                        e.printStackTrace();
                }
       
                Document doc = null;
       
                try {
                        doc = builder.parse(configFile);
                } catch (SAXException e) {
                        e.printStackTrace();
                } catch (IOException e) {
                        e.printStackTrace();
                }
        doc.getDocumentElement().normalize();
       
        NodeList nodes = doc.getElementsByTagName("window");
        for (int i = 0; i < nodes.getLength(); i++) {
           Node mynode = nodes.item(i);
           System.out.println("Property = " + mynode.getNodeName());
           
           if (mynode.getNodeType() == Node.ELEMENT_NODE) {
               Element myelement = (Element) mynode;

               System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());
               System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());
               System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent());
           } // close if
        } // close for
    } // close method
} //close class

使用 Java 更新 XML

用户时不时地会改变某个偏好项,这时候 org.w3c.dom 库就可以帮助你更新某个 XML 元素的内容。你只需要选择这个 XML 元素,就像你读取它时那样。不过,此时你不再使用 .getTextContent 方法,而是使用 .setTextContent 方法。

updatePref = myelement.getElementsByTagName("fullscreen").item(0);
updatePref.setTextContent("1");

System.out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());  

这么做会改变应用程序内存中的 XML 文档,但是还没有把数据写回到磁盘上。配合使用 javax 和 w3c 库,你就可以把读取到的 XML 内容写回到配置文件中。

TransformerFactory transformerFactory = TransformerFactory.newInstance();

Transformer xtransform;
xtransform = transformerFactory.newTransformer();

DOMSource mydom = new DOMSource(doc);
StreamResult streamResult = new StreamResult(configFile);

xtransform.transform(mydom, streamResult);

这么做会没有警告地写入转换后的数据,并覆盖掉之前的配置。

下面是完整的代码,包括更新 XML 的操作:

package myConfigParser;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class ConfigParser {

        public static void main(String[] args) {
                Path configPath = Paths.get(System.getProperty("user.home"), ".config", "DemoXMLParser");
                File configFile = new File(configPath.toString(), "myconfig.xml");
                DocumentBuilderFactory factory =
                DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = null;
               
                try {
                        builder = factory.newDocumentBuilder();
                } catch (ParserConfigurationException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
       
                Document doc = null;
       
                try {
                        doc = builder.parse(configFile);
                } catch (SAXException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
        doc.getDocumentElement().normalize();
        Node updatePref = null;
//        NodeList nodes = doc.getChildNodes();
        NodeList nodes = doc.getElementsByTagName("window");
        for (int i = 0; i < nodes.getLength(); i++) {
           Node mynode = nodes.item(i);
           System.out.println("Property = " + mynode.getNodeName());
           
           if (mynode.getNodeType() == Node.ELEMENT_NODE) {
               Element myelement = (Element) mynode;

               System.out.println("Theme = " + myelement.getElementsByTagName("theme").item(0).getTextContent());
               System.out.println("Fullscreen = " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());
               System.out.println("Icon set = " + myelement.getElementsByTagName("icons").item(0).getTextContent());

               updatePref = myelement.getElementsByTagName("fullscreen").item(0);
               updatePref.setTextContent("2");
               System.out.println("Updated fullscreen to " + myelement.getElementsByTagName("fullscreen").item(0).getTextContent());          
           } // close if
           
        }// close for

        // write DOM back to the file
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer xtransform;

        DOMSource mydom = new DOMSource(doc);
        StreamResult streamResult = new StreamResult(configFile);

        try {
                xtransform = transformerFactory.newTransformer();
                xtransform.transform(mydom, streamResult);
        } catch (TransformerException e) {
                e.printStackTrace();
        }
                       
    } // close method
} //close class

如何保证配置不出问题

编写配置文件看上去是一个还挺简单的任务。一开始,你可能会用一个简单的文本格式,因为你的应用程序只要寥寥几个配置项而已。但是,随着你引入了更多的配置项,读取或者写入错误的数据可能会给你的应用程序带来意料之外的错误。一种帮助你保持配置过程安全、不出错的方法,就是使用类似 XML 的规范格式,然后依靠你用的编程语言的内置功能来处理这些复杂的事情。

这也正是我喜欢使用 Java 和 XML 的原因。每当我试图读取错误的配置值时,Java 就会提醒我。通常,这是由于我在代码中试图获取的节点,并不存在于我期望的 XML 路径中。XML 这种高度结构化的格式帮助了代码保持可靠性,这对用户和开发者来说都是有好处的。


via: https://opensource.com/article/21/7/parsing-config-files-java

作者:Seth Kenlon 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

将这些命令加入到你的工作流中,使 Git 发挥更大的作用。

 title=

如果你经常使用 Git,你可能会知道它非常有名。它可能是最受欢迎的版本控制方案,它被一些 最大的软件项目 用来 跟踪文件变更。Git 提供了 健壮的界面 来审阅代码、把实验性的变更合并到已经存在的文件中。得益于 Git 钩子,它以灵活性而闻名。同时,也因为它的强大,它给人们留下了一个“复杂”的印象。

Git 有诸多特性,你不必全部使用,但是如果你正在深入研究 Git 的 子命令 subcommands ,我这里倒是有几个,或许你会觉得有用。

1、找到变更

如果你已经熟悉 Git 的基本指令(fetch、add、commit、push、log 等等),但是希望学习更多,那么从 Git 的检索子命令开始是一个简单安全的选择。检索你的 Git 仓库(你的 工作树)并不会做出任何更改,它只是一个报告机制。你不会像使用 git checkout 一样承担数据完整性的风险,你只是在向 Git 请求仓库的当前状态和历史记录而已。

git whatchanged 命令(几乎本身就是一个助记符)可以查看哪些文件在某个 提交 commit 中有变更、分别做了什么变更。它是一个简单的、用户友好的命令,因为它把 show、diff-tree 和 log 这三个命令的最佳功能整合到了一个好记的命令中。

2、使用 git stash 管理变更

你越多地使用 Git,你就会使用 Git 越多。这就是说,一旦你习惯了 Git 的强大功能,你就会更频繁地使用它。有时,你正在处理一大堆文件,忽然意识到了有更紧急的任务要做。这时,在 git stash 的帮助下,你就可以把所有正在进行的工作收集起来,然后安全地 暂存 stash 它们。当你的工作空间变得整洁有序,你就可以把注意力放到别的任务上,晚些时候再把暂存的文件重新加载到工作树里,继续之前的工作。

3、使用 git worktree 来得到链接的副本

当 git stash 不够用的时候,Git 还提供了强大的 git worktree 命令。有了它,你可以新建一个 链接的 仓库 副本 clone ,组成一个新分支,把 HEAD 设置到任意一个提交上,然后基于这个分支开始你的新工作。在这个链接的副本里,你可以进行和主副本完全不同的任务。这是一个避免意外的变更影响当前工作的好办法。当你完成了你的新工作,你可以把新分支推送到远程仓库;也可以把当前的变更归档,晚些时候再处理;还可以从别的工作树中获取它们的变更。无论选择哪一种,你的工作空间之间都会保持相互隔离,任一空间中的变更都不会影响其他空间中的变更,直到你准备好了要合并它们。

4、使用 git cherry-pick 来选择合并

这可能听起来很反直觉,但是,你的 Git 水平越高,你可能遇到的合并冲突就会越多。这是因为合并冲突不一定是错误的标志,而是活跃的标志。在学习 Git 中,适应合并时的冲突,并学会如何解决它们是非常重要的。通常的方式或许够用,但是有时候你会需要更加灵活地进行合并,这时候就该 git cherry-pick 出场了。遴选操作允许你选择部分合并提交,这样一来你就不需要因为一些细微的不协调而拒绝整个合并请求了。

5、使用 Git 来管理 $HOME

使用 Git 来管理你的主目录从来没有这么简单过,这都要归功于 Git 可以自由选择管理对象的能力,这是一个在多台计算机之间保持同步的现实可行的选项。但是,想要让它工作顺利,你必须非常明智且谨慎才行。如果你想要了解更多,点击阅读我写的关于 使用 Git 来管理 $HOME 的小技巧。

更好地使用 Git

Git 是一个强大的版本控制系统,你使用得越熟练,就可以越轻松地借助它来完成复杂的任务。今天就尝试一些新的 Git 命令吧,欢迎在评论区分享你最喜欢的 Git 命令。


via: https://opensource.com/article/21/4/git-commands

作者:Seth Kenlon 选题:lujun9972 译者:lkxed 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出