蚊章汇 - Groovy
type: channel
link: https://linuxcn.bwsl.wang/tag/Groovy/
items: 为你的 awk 脚本注入 Groovy
items: 我如何使用 Groovy 分析我的音乐目录
items: 在 Java 和 Groovy 中创建和初始化列表的不同
items: 在 Groovy 和 Java 中创建并初始化映射的不同
items: 用 Groovy 解析 JSON 配置文件
items: 在 Grails 中使用 jQuery 和 DataTables

为你的 awk 脚本注入 Groovy
type: item
date: 2022-11-09T18:01:00+08:00
description: awk 和 Groovy 相辅相成，可以创建强大、有用的脚本。最近我写了一个使用 Groovy 脚本来清理我的音乐文件中的标签的系列。我开发了一个 框架，可以识别我的音乐目录的结构，并使用它来遍历音乐文件。在该系列的最后一篇文章中，我从框架中分离出一个实用类，我的脚本可以用它来处理文件。这个独立的框架让我想起了很多 awk 的工作方式。对于那些不熟悉 awk 的人来说，你学习下这本电子书：《awk 实用指南》我从 1984 年开始大量使用 awk，当时我们的小公司买了第一台“真正的”计算机，它运行的是 System V Unix。对我来说，awk 是非常完美的：它有 关联内存 associative memory ——将数组视为由字符串而不是数字来索引的。它内置了正则表达式，似乎专为处理数据而生，尤其是在处理数据列时，而且结构紧凑，易于学习。最后，它非常适合在 Unix 工作流使用，从标准输入或文件中读取数据并写入到输出，数据不需要经过其他的转换就出现在了输入流中。说 awk 是我日常计算工具箱中的一个重要部分一点也不为过。然而，在我使用 awk 的过程中，有几件事让我感到不满意。可能主要的问题是 awk 善于处理以分隔字段呈现的数据，但很奇怪它不善于处理 CSV 文件，因为 CSV 文件的字段被引号包围时可以嵌入逗号分隔符。另外，自 awk 发明以来，正则表达式已经有了很大的发展，我们需要记住两套正则表达式的语法规则，而这并不利于编写无 bug 的代码。一套这样的规则已经很糟糕了。由于 awk 是一门简洁的语言，因此它缺少很多我认为有用的东西，比如更丰富的基础类型、结构体、switch 语句等等。相比之下，Groovy 拥有这些能力：可以使用 OpenCSV 库，它很擅长处理 CSV 文件、Java 正则表达式和强大的匹配运算符、丰富的基础类型、类、switch 语句等等。Groovy 所缺乏的是简单的面向管道的概念，即把要处理数据作为一个传入的流，以及把处理过的数据作为一个传出的流。但我的音乐目录处理框架让我想到，也许我可以创建一个 Groovy 版本的 awk “引擎”。这就是我写这篇文章的目的。安装 Java 和 GroovyGroovy 是基于 Java 的，需要先安装 Java。最新的、合适的 Java 和 Groovy 版本可能都在你的 Linux 发行版的软件库中。Groovy 也可以按照 Groovy 主页 上的说明进行安装。对于 Linux 用户来说，一个不错的选择是 SDKMan，它可以用来获得多个版本的 Java、Groovy 和其他许多相关工具。在这篇文章中，我使用的是 SDK 的版本：Java：OpenJDK 11 的 11.0.12 的开源版本Groovy：3.0.8使用 Groovy 创建 awk这里的基本想法是将打开一个或多个文件进行处理、将每行分割成字段、以及提供对数据流的访问等复杂情况封装在三个部分：在处理数据之前在处理每行数据时在处理完所有数据之后我并不打算用 Groovy 来取代 awk。相反，我只是在努力实现我的典型用例，那就是：使用一个脚本文件而不是在命令行写代码处理一个或多个输入文件设置默认的分隔符为 |，并基于这个分隔符分割所有行使用 OpenCSV 完成分割工作（awk 做不到）框架类下面是用 Groovy 类实现的 “awk 引擎”：@Grab('com.opencsv:opencsv:5.6') import com.opencsv.CSVReader public class AwkEngine { // With admiration and respect for // Alfred Aho // Peter Weinberger // Brian Kernighan // Thank you for the enormous value // brought my job by the awk // programming language Closure onBegin Closure onEachLine Closure onEnd private String fieldSeparator private boolean isFirstLineHeader private ArrayList<String> fileNameList public AwkEngine(args) { this.fileNameList = args this.fieldSeparator = "|" this.isFirstLineHeader = false } public AwkEngine(args, fieldSeparator) { this.fileNameLi…

我如何使用 Groovy 分析我的音乐目录
type: item
date: 2022-09-06T17:12:00+08:00
description: 为了简化 Java 的繁琐，我制作了一个 Groovy 工具来分析我的音乐目录。最近，我一直在研究 Groovy 是如何简化略微繁琐的 Java 的。在这篇文章中，我开始了一个简短的系列，通过创建一个分析我的音乐目录的工具来演示 Groovy 脚本。在本文中，我将演示 groovy.File 类如何扩展和精简 java.File 并简化其使用。这为查看音乐文件夹的内容提供了一个框架，以确保预期的内容（例如，cover.jpg 文件）就位。我使用 JAudiotagger 库 来分析音乐文件的标签。安装 Java 和 GroovyGroovy 基于 Java，需要安装 Java。 Java 和 Groovy 的最新和稳定的版本可能都在你的 Linux 发行版的仓库中。 Groovy 也可以直接从 Apache Foundation 网站 安装。对于 Linux 用户来说，一个不错的选择是 SDKMan，它可用于获取 Java、Groovy 和许多其他相关工具的多个版本。对于本文，我使用以下 SDK 版本：Java：版本 11.0.12-open 的 OpenJDK 11Groovy：版本 3.0.8音乐元数据最近，我重整了我的音乐消费方式。我决定使用优秀的开源 Cantata 音乐播放器，它是开源 MPD 音乐播放器 的一个前端。我所有的电脑的音乐都存储在 /var/lib/mpd/music 目录下。在该音乐目录下有艺术家子目录，在每个艺术家子目录下有专辑子目录，包含音乐文件、cover.jpg，偶尔还有 PDF 格式的内页说明。我绝大部分的音乐文件都是 FLAC 格式的，有一些是 MP3 格式，可能还有一小部分是 OGG 格式。我选择 JAudiotagger 库的一个原因是它可以透明地处理不同的标签格式。当然，JAudiotagger 是开源的！那么查看音频标签有什么意义呢？以我的经验，音频标签的管理极差。（提到音频标签，）我的脑海中浮现出“粗心”这个词。这是标签本身真正存在的问题，也可能是出于我自己的学究倾向。无论如何，这是一个可以通过使用 Groovy 和 JAudiotagger 解决的重要问题。不过，它不仅适用于音乐收藏。许多其他现实世界的问题也适用，如需要下沉到文件系统中的目录树来处理在那里找到的内容。使用 Groovy 脚本这是此任务所需的基本代码。我在脚本中加入了注释，这些注释反映了我通常留给自己的（相对简写的）“注释提醒”：// 定义音乐库目录 def musicLibraryDirName = '/var/lib/mpd/music' // 输出 CSV 文件标题行 println "artistDir|albumDir|contentFile" // 迭代音乐库目录中的每个目录 // 这一层应该是艺术家目录 new File(musicLibraryDirName).eachDir { artistDir -> // 迭代艺术家目录中的每个目录 // 这一层应该是专辑目录 artistDir.eachDir { albumDir -> // 迭代专辑目录中的每个目录 // 这里应该是内容 // 或相关内容（如 `cover.jpg`，PDF 格式的内页说明） albumDir.eachFile { contentFile -> println "$artistDir.name|$albumDir.name|$contentFile.name" } } } 如上所述，我使用 groovy.File 在目录树中移动。具体来说：第 7 行创建一个新的 groovy.File 对象并在其上调用 groovy.File.eachDir()，第 7 行的 { 和第 18 行的结尾的 } 之间的代码是传给 eachDir() 的 groovy.Colsue 参数。这意味着 eachDir() 为目录中找到的每个子目录执行该代码。这类似于 Java lambda（也称为“匿名函数”）。 Groovy 闭包不会像 lambda 那样限制对调用环境的访问（在最新版本的 Groovy 中，如果你愿意，也可以使用 Java lambda）。如上所述，音乐库目录中的子目录应该是艺术家目录（例如，“Iron Butterfly” 或 “Giacomo Puccini”），因此 artistDir 是 eachDir() 传递给闭包的参数。第 10 行对每个 artistDir 调用 eachDir()，第 10 行的 { 和第 17 行的 } 之间的代码形成另一个处理 albumDir 的闭包。第 14 行，在每个 albumDir 上调用 eachFile()，第 14 行的 { 和第 16 行的 } 之间的代码形成了处理专辑内…

在 Java 和 Groovy 中创建和初始化列表的不同
type: item
date: 2022-03-30T05:40:29+08:00
description: 首先在 Java 中创建初始化一个整数列表，然后在 Groovy 中做同样的事。我非常喜欢 Groovy 编程语言。我喜欢它是因为我喜欢 Java，尽管 Java 有时候感觉很笨拙。正因为我是那么喜欢 Java，其他运行在 JVM 上语言都不能吸引我。比方说 Kotlin、Scala 还有 Clojure 语言，它们感觉上就和 Java 不一样，因为它们对于什么是好的编程语言的理解不同。Groovy 和它们都不一样，在我看来，Groovy 是一个完美的选项，特别是对于一部分程序员来说，他们喜欢 Java，但是又需要一个更灵活、更紧凑，并且有时候更直接的语言。 列表 List 这种数据结构是一个很好的例子，它可以容纳一个无序的列表，列表中的元素可以是数字、字符串或者对象，程序员可以用某种方式高效地遍历这些元素，特别是对于编写和维护脚本的人来说，“高效”的关键就是要有简洁清晰的表达，而不需要一大堆“仪式”，把代码的意图都变模糊了。安装 Java 和 GroovyGroovy 是基于 Java 的，因此需要同时安装一个 Java 才行。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 版本。或者，你也可以在根据 这些指导 来安装 Groovy。对于 Linux 用户来说，SDKMan 是一个不错的代替选项，你可以使用它来获取多个 Java 和 Groovy 版本，以及许多其他的相关工具。在这篇文章中，我使用的 SDK 发行版是：Java: OpenJDK 11 的 11.0.12-open 版本Groovy: 3.0.8 版本言归正传Java 中有很多方法可以实例化并初始化列表，从它最初被引入的时候就有了（我记得是在 Java 1.5 的时候，但请不要引用我的话）。在这些方法里，有两个有趣的方法，它们涉及到了 java.util.Arrays 和 java.util.List 这两个类。使用 java.util.Arrays 类java.util.Arrays 类定义了一个 asList() 静态方法，它可以被用来创建一个基于数组的列表，因此大小是不可变的，尽管其中的元素是可以被修改的。下面是它的使用方式：var a1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10); // immutable list of mutable elements System.out.println("a1 = " + a1); System.out.println("a1 is an instance of " + a1.getClass()); // output is // a1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // a1 is an instance of class java.util.Arrays$ArrayList a1.set(0,0); // succeeds System.out.println("a1 = " + a1); // output is // a1 = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10] a1.add(11); // fails producing // Exception in thread "main" java.lang.UnsupportedOperationException System.out.println("a1 = " + a1); // not reached 使用 java.util.List 类java.util.List 类定义了一个 of() 静态方法，它可以被用来创建一个不可变的列表，其中的元素是否可变要取决于它们本身是否支持修改。下面是它的使用方式：var a2 = List.of(1,2,3,4,5,6,7,8,9,10); System.out.println("a2 = " + a2); System.out.println("a2 is an instance of " + a2.getClass()); // output is // a2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // a2 is an instance of class java.util.ImmutableCollections$ListN a2.set(0,0); // fails producing // Exception in thread "main" java.lang.UnsupportedOperationException System.out.println("a2 = " + a2); // not reached a2.add(11); // als…

在 Groovy 和 Java 中创建并初始化映射的不同
type: item
date: 2022-03-21T17:25:38+08:00
description: Java 和 Groovy 中的 映射 map 都是非常通用的，它允许 关键字 key 和 值 value 为任意类型，只要继承了 Object 类即可。我最近在探索 Java 与 Groovy 在 创建并初始化 列表 List 和 在运行时构建 列表 List 方面的一些差异。我观察到，就实现这些功能而言，Groovy 的简洁和 Java 的繁复形成了鲜明对比。在这篇文章中，我将实现在 Java 和 Groovy 中创建并初始化 映射 Map 。映射为开发支持根据 关键字 key 检索的结构提供了可能，如果找到了这样一个关键字，它就会返回对应的 值 value 。今天，很多编程语言都实现了映射，其中包括 Java 和 Groovy，也包括了 Python（它将映射称为 字典 dict ）、Perl、awk 以及许多其他语言。另一个经常被用来描述映射的术语是 关联数组 associative array ，你可以在 这篇维基百科文章 中了解更多。Java 和 Groovy 中的映射都是非常通用的，它允许关键字和值为任意类型，只要继承了 Object 类即可。安装 Java 和 GroovyGroovy 基于 Java，因此你需要先安装 Java。你的 Linux 发行版的仓库中可能有最近的比较好的 Java 和 Groovy 版本。或者，你也可以在根据上面链接中的指示来安装 Groovy。对于 Linux 用户来说，SDKMan 是一个不错的代替选项，你可以使用它来获取多个 Java 和 Groovy 版本，以及许多其他的相关工具。在这篇文章中，我使用的 SDK 发行版是：Java: version 11.0.12-open of OpenJDK 11;Groovy: version 3.0.8.言归正传Java 提供了非常多的方式来实例化和初始化映射，并且从 Java 9 之后，添加了一些新的方式。其中最明显的方式就是使用 java.util.Map.of() 这个静态方法，下面介绍如何使用它：var m1 = Map.of( "AF", "Afghanistan", "AX", "Åland Islands", "AL", "Albania", "DZ", "Algeria", "AS", "American Samoa", "AD", "Andorra", "AO", "Angola", "AI", "Anguilla", "AQ", "Antarctica"); System.out.println("m1 = " + m1); System.out.println("m1 is an instance of " + m1.getClass()); 事实证明，在此种情况下，Map.of() 有两个重要的限制。其一，这样创建出来的映射实例是 不可变的 immutable 。其二，你最多只能提供 20 个参数，用来表示 10 个 键值对 key-value pair 。你可以尝试着添加第 10 对和第 11 对，比方说 "AG", "Antigua and Barbuda" 和 "AR", "Argentina"，然后观察会发生什么。你将发现 Java 编译器尝试寻找一个支持 11 个键值对的 Map.of() 方法而遭遇失败。快速查看 java.util.Map 类的文档，你就会找到上述第二个限制的原因，以及解决这个难题的一种方式：var m2 = Map.ofEntries( Map.entry("AF", "Afghanistan"), Map.entry("AX", "Åland Islands"), Map.entry("AL", "Albania"), Map.entry("DZ", "Algeria"), Map.entry("AS", "American Samoa"), Map.entry("AD", "Andorra"), Map.entry("AO", "Angola"), Map.entry("AI", "Anguilla"), Map.entry("AQ", "Antarctica"), Map.entry("AG", "Antigua and Barbuda"), Map.entry("AR", "Argentina"), Map.entry("AM", "Armenia"), Map.entry("AW", "Aruba"), Map.entry("AU", "Australia"), Map.entry("AT", "Austria"), Map.entry("AZ", "Azerbaijan"), Map.entry("BS", "Bahamas"), Map.entry("BH", "B…

用 Groovy 解析 JSON 配置文件
type: item
date: 2021-07-13T07:24:38+08:00
description: 抛开关于是否使用 JSON 作为配置格式的争论，只需学习如何用 Groovy 来解析它。应用程序通常包括某种类型的默认或“开箱即用”的状态或配置，以及某种让用户根据自己的需要定制配置的方式。例如，LibreOffice Writer 通过其菜单栏上的工具 > 选项，可以访问诸如用户数据、字体、语言设置等（以及更多的）设置。一些应用程序（如 LibreOffice）提供了一个点选式的用户界面来管理这些设置。有些，像 Tracker（GNOME 的“任务”，用于索引文件）使用 XML 文件。还有一些，特别是基于 JavaScript 的应用，使用 JSON，尽管它有许多人抗议（例如，这位作者 和 这位其他作者）。在这篇文章中，我将回避关于是否使用 JSON 作为配置文件格式的争论，并解释如何使用 Groovy 编程语言 来解析这类信息。Groovy 以 Java 为基础，但有一套不同的设计重点，使 Groovy 感觉更像 Python。安装 Groovy由于 Groovy 是基于 Java 的，它也需要安装 Java。你可能会在你的 Linux 发行版的软件库中找到最近的、合适的 Java 和 Groovy 版本。或者，你可以按照其网站上的 说明 安装 Groovy。 Linux 用户的一个不错的选择是 SDKMan，你可以使用它来获取 Java、Groovy 和许多其他相关工具的多个版本。 对于本文，我将使用我的发行版的 OpenJDK11 和 SDKMan 的 Groovy 3.0.7。演示的 JSON 配置文件在这个演示中，我从 Drupal 中截取了这个 JSON 文件，它是 Drupal CMS 使用的主要配置文件，并将其保存在文件 config.json 中：{ "vm": { "ip": "192.168.44.44", "memory": "1024", "synced_folders": [ { "host_path": "data/", "guest_path": "/var/www", "type": "default" } ], "forwarded_ports": [] }, "vdd": { "sites": { "drupal8": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 8", "site_mail": "box@example.com", "vhost": { "document_root": "drupal8", "url": "drupal8.dev", "alias": ["www.drupal8.dev"] } }, "drupal7": { "account_name": "root", "account_pass": "root", "account_mail": "box@example.com", "site_name": "Drupal 7", "site_mail": "box@example.com", "vhost": { "document_root": "drupal7", "url": "drupal7.dev", "alias": ["www.drupal7.dev"] } } } } } 这是一个漂亮的、复杂的 JSON 文件，有几层结构，如：<>.vdd.sites.drupal8.account_name 和一些列表，如：<>.vm.synced_folders 这里，<> 代表未命名的顶层。让我们看看 Groovy 是如何处理的。用 Groovy 解析 JSONGroovy 自带的 groovy.json 包，里面有各种很酷的东西。其中最好的部分是 JsonSlurper 类，它包括几个 parse() 方法，可以将 JSON 转换为 Groovy 的 Map，一种根据键值存储的数据结构。下面是一个简短的 Groovy 程序，名为 config1.groovy，它创建了一个 JsonSlurper 实例，然后调用其中的 parse() 方法来解析文件中的 JSON，并将其转换名为 config 的 Map 实例，最后将该 map 输出：import groovy.json.JsonSlurper def jsonSlurper = new JsonSlurper() def config = jsonSlurper.parse(new File('config.json')) println "config = $config" 在终端的命令行上运行这个程序：$ groovy config…

在 Grails 中使用 jQuery 和 DataTables
type: item
date: 2018-11-24T18:24:47+08:00
description: 本文介绍如何构建一个基于 Grails 的数据浏览器来可视化复杂的表格数据。我是 Grails 的忠实粉丝。当然，我主要是热衷于利用命令行工具来探索和分析数据的数据从业人员。数据从业人员经常需要查看数据，这也意味着他们通常拥有优秀的数据浏览器。利用 Grails、jQuery，以及 DataTables jQuery 插件，我们可以制作出非常友好的表格数据浏览器。DataTables 网站提供了许多“食谱式”的教程文档，展示了如何组合一些优秀的示例应用程序，这些程序包含了完成一些非常漂亮的东西所必要的 JavaScript、HTML，以及偶尔出现的 PHP。但对于那些宁愿使用 Grails 作为后端的人来说，有必要进行一些说明示教。此外，样本程序中使用的数据是一个虚构公司的员工的单个平面表格数据，因此处理这些复杂的表关系可以作为读者的一个练习项目。本文中，我们将创建具有略微复杂的数据结构和 DataTables 浏览器的 Grails 应用程序。我们将介绍 Grails 标准，它是 Groovy 式的 Java Hibernate 标准。我已将代码托管在 GitHub 上方便大家访问，因此本文主要是对代码细节的解读。首先，你需要配置 Java、Groovy、Grails 的使用环境。对于 Grails，我倾向于使用终端窗口和 Vim，本文也使用它们。为获得现代的 Java 环境，建议下载并安装 Linux 发行版提供的 Open Java Development Kit （OpenJDK）（应该是 Java 8、9、10 或 11 之一，撰写本文时，我正在使用 Java 8）。从我的角度来看，获取最新的 Groovy 和 Grails 的最佳方法是使用 SDKMAN!。从未尝试过 Grails 的读者可能需要做一些背景资料阅读。作为初学者，推荐文章 创建你的第一个 Grails 应用程序。获取员工信息浏览器应用程序正如上文所提，我将本文中员工信息浏览器的源代码托管在 GitHub上。进一步讲，应用程序 embrow 是在 Linux 终端中用如下命令构建的：cd Projects grails create-app com.nuevaconsulting.embrow域类和单元测试创建如下：grails create-domain-class com.nuevaconsulting.embrow.Position grails create-domain-class com.nuevaconsulting.embrow.Office grails create-domain-class com.nuevaconsulting.embrow.Employeecd embrowgrails createdomaincom.grails createdomaincom.grails createdomaincom.这种方式构建的域类没有属性，因此必须按如下方式编辑它们：Position 域类：package com.nuevaconsulting.embrow class Position { String name int starting static constraints = { name nullable: false, blank: false starting nullable: false } }com.Stringint startingstatic constraintsnullableblankstarting nullableOffice 域类：package com.nuevaconsulting.embrow class Office { String name String address String city String country static constraints = { name nullable: false, blank: false address nullable: false, blank: false city nullable: false, blank: false country nullable: false, blank: false } }Enployee 域类：package com.nuevaconsulting.embrow class Employee { String surname String givenNames Position position Office office int extension Date hired int salary static constraints = { surname nullable: false, bl…