
本文介绍在 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 <p>? <strong>核心要点与注意事项:</strong> </p>
<ul>
<li>
<strong>不要预处理换行符</strong>:issue.getDescription() 返回的就是带
的原始字符串,Groovy 的 splitEachLine() 专为此设计,直接使用即可,无需 replaceAll “扁平化”。 </li>
<li>
<strong>避免过度依赖复杂正则</strong>:原问题中提供的 regex(如 /*?|+*?/)易受空白、嵌套星号干扰;改用 split('\|') + trim() 组合更清晰、可维护。 </li>
<li>
<strong>表头识别要显式</strong>:通过 line.trim().startsWith('||') 判断表头,比依赖行号更鲁棒(防止空行干扰)。 </li>
<li>
<strong>字段清洗不可少</strong>:Markdown 表格中 |*Task 1*| 需 replace('*', '') 并 trim(),否则 "*Task 1*" 会作为 title 写入 JSON。 </li>
<li>
<strong>空值容错</strong>:使用 findAll { it } 过滤空单元格,避免 IndexOutOfBoundsException。 </li>
</ul>
<p>? 总结:所谓“转为单行”,本质是消除换行符对解析流程的干扰。而 Groovy 的 splitEachLine() 正是为此场景优化的 API —— 它将多行字符串视为行序列,而非需人工拼接的单行字符串。坚持这一语义,代码更简洁、健壮性更高,也完全规避了因
差异引发的跨平台兼容问题。</p></map>










