
本文介绍在 Java/Groovy(如 Jira ScriptRunner)中,如何将含 换行符的多行 Markdown 表格字符串规范化为逻辑单行(保留原始分隔符),从而确保正则或 splitEachLine 等解析逻辑稳定可靠。重点解决换行符被提前解释导致匹配失败的问题。
本文介绍在 java/groovy(如 jira scriptrunner)中,如何将含 ` ` 换行符的多行 markdown 表格字符串规范化为逻辑单行(保留原始分隔符),从而确保正则或 `spliteachline` 等解析逻辑稳定可靠。重点解决换行符被提前解释导致匹配失败的问题。
在 Jira ScriptRunner 等 Groovy 运行环境中处理 issue 描述字段时,常遇到一个典型问题:原始 Markdown 表格以多行形式存储(含 ),但 Groovy 字符串字面量或日志输出中这些换行符会被实际解析为换行,导致后续基于正则或按行分割(如 splitEachLine())的解析逻辑失效——尤其当正则设计依赖“单行内匹配”或 splitEachLine 期望统一分隔符时。
关键认知:无需真正“删除换行”,而是确保换行符作为可预测、可控制的字符串字符参与处理。
Groovy 中的 String.splitEachLine(pattern) 方法天然适配多行文本,它会按行分割后再对每行应用正则捕获,完全不需要手动将多行“压缩”为单行。强行用 replaceAll("[\r\n]+", " ") 或类似方式合并,反而会破坏表格结构(例如将 |*Task 4*| 15/03/23|... 错误地压成一行后导致列错位),且引入空格污染,使正则更难健壮。
✅ 推荐方案:直接使用 splitEachLine() 配合稳健分隔逻辑
以下为生产就绪的 Groovy 示例(兼容 Jira Server ScriptRunner):
import groovy.json.JsonOutput
// 模拟从 issue.getDescription() 获取的原始多行字符串(含
)
String description = """|| ||Due Date||Done on||OK/NOK||Remarks||
|*Task 1*| | | | |
|*Task 2*| | | | |
|*Task 3*| | | | |
|*Task 4*| 15/03/23| | | See document X|"""
List<Map> actionItems = []
// 使用 splitEachLine 安全按行处理;正则匹配每行中的单元格内容
// 注意:此处用 "\|\*" 和 "\*" 分别捕获开头和结尾的 *,再 trim 去除多余空格与星号
description.splitEachLine() { line ->
// 跳过表头行(首行含 || ||...)
if (line.trim().startsWith('||')) return
// 提取各列:利用 | 分割,去除首尾 |,再逐字段清理
def cells = line.split('\|').collect { it.trim() }.findAll { it } // 过滤空字符串
if (cells.size() >= 5) {
def title = cells[0].replace('*', '').trim()
def dueDate = cells[1].trim()
def doneOn = cells[2].trim()
def okNok = cells[3].trim()
def remarks = cells[4].trim()
actionItems << [
'TITLE': title,
'DUE DATE': dueDate,
'DONE ON': doneOn,
'NOTE': remarks
]
}
}
// 构建标准 JSON 结构
def result = [ACTION: actionItems]
log.info(JsonOutput.prettyPrint(JsonOutput.toJson(result)))? 核心要点与注意事项:
- 不要预处理换行符:issue.getDescription() 返回的就是带 的原始字符串,Groovy 的 splitEachLine() 专为此设计,直接使用即可,无需 replaceAll “扁平化”。
- 避免过度依赖复杂正则:原问题中提供的 regex(如 /*?|+*?/)易受空白、嵌套星号干扰;改用 split('\|') + trim() 组合更清晰、可维护。
- 表头识别要显式:通过 line.trim().startsWith('||') 判断表头,比依赖行号更鲁棒(防止空行干扰)。
- 字段清洗不可少:Markdown 表格中 |*Task 1*| 需 replace('*', '') 并 trim(),否则 "*Task 1*" 会作为 title 写入 JSON。
- 空值容错:使用 findAll { it } 过滤空单元格,避免 IndexOutOfBoundsException。
? 总结:所谓“转为单行”,本质是消除换行符对解析流程的干扰。而 Groovy 的 splitEachLine() 正是为此场景优化的 API —— 它将多行字符串视为行序列,而非需人工拼接的单行字符串。坚持这一语义,代码更简洁、健壮性更高,也完全规避了因 差异引发的跨平台兼容问题。

















