我目前正在使用API Apache POI,并尝试使用它来编辑Word文档(*.docx
)。文档由段落(在XWPFParagraph
个对象中)组成,段落包含嵌入在'运行中的文本。 (XWPFRun
)。一个段落可以有很多次运行(取决于文本属性,但它有时是随机的)。在我的文档中,我可以使用特定标记来替换数据(我的所有标记都遵循此模式<#TAG_NAME#>
)
例如,如果我处理包含文本Some text with a tag <#SOMETAG#>
的段落,我可以得到类似的内容
XWPFParagraph paragraph = ... // Get a paragraph from the document
System.out.println(paragraph.getText());
// Prints: Some text with a tag <#SOMETAG#>
但是,如果我想编辑该段落的文本,我需要处理运行并且运行次数不固定。因此,如果我用该代码显示运行的内容:
System.out.println("Number of runs: " + paragraph.getRuns().size());
for (XWPFRun run : paragraph.getRuns()) {
System.out.println(run.text());
}
有时它可能是这样的:
// Output:
// Number of runs: 1
// Some text with a tag <#SOMETAG#>
和其他时间一样
// Output:
// Number of runs: 4
// Some text with a tag
// <#
// SOMETAG
// #>
我需要做的是获得包含标记开头的第一次运行以及包含标记其余部分的以下运行的索引(如果标记在多次运行中分割)。我设法获得该算法的第一个版本,但只有在标记的开头(<#
)和标记的结尾(#>
)没有划分时才有效。 Here's what I've already done
所以我想得到的是一个能够管理该问题的算法,如果可能的话,它可以使用任何给定的标记(不一定是<#
和#>
,所以我可以用类似的东西替换这个{{{
和此}}}
)。
对不起,如果我的英语不是很完美,请不要犹豫,让我澄清你想要的任何一点。
答案 0 :(得分:2)
最后我自己找到了答案,我完全改变了我对原始算法的思考方式(我对它进行了评论,因此它可以帮助那些可能处于同样情况的人)
// Before using the function, I'm sure that:
// paragraph.getText().contains(surroundedTag) == true
private void editParagraphWithData(XWPFParagraph paragraph, String surroundedTag, String replacement) {
List<Integer> runsToRemove = new LinkedList<Integer>();
StringBuilder tmpText = new StringBuilder();
int runCursor = 0;
// Processing (in normal order) the all runs until I found my surroundedTag
while (!tmpText.toString().contains(surroundedTag)) {
tmpText.append(paragraph.getRuns().get(runCursor).text());
runsToRemove.add(runCursor);
runCursor++;
}
tmpText = new StringBuilder();
// Processing back (in reverse order) to only keep the runs I need to edit/remove
while (!tmpText.toString().contains(surroundedTag)) {
runCursor--;
tmpText.insert(0, paragraph.getRuns().get(runCursor).text());
}
// Edit the first run of the tag
XWPFRun runToEdit = paragraph.getRuns().get(runCursor);
runToEdit.setText(tmpText.toString().replaceAll(surroundedTag, replacement), 0);
// Forget the runs I don't to remove
while (runCursor >= 0) {
runsToRemove.remove(0);
runCursor--;
}
// Remove the unused runs
Collections.reverse(runsToRemove);
for (Integer runToRemove : runsToRemove) {
paragraph.removeRun(runToRemove);
}
}
所以现在我处理段落的所有运行,直到找到我的包围标签,然后我处理回段落忽略第一次运行,如果我不需要编辑它们。