原文标题:Creating Word documents with Docx4j

原文链接:http://blog.iprofs.nl/2012/09/06/creating-word-documents-with-docx4j/

原文作者:lvdpal

发表时间:2012年9月6日

注:第一次翻译博客;由于个人水平不高,而且英语仅有四级水平,所以错误很多,请大家见谅!!!只是国内关于docx4j的博客极少,感觉这篇入门博客挺不错,勉强翻译过来,希望对大家有所帮助。


一段时间之前,我为一个想要在word和excel中作报表的客户提供服务。我以前的项目中生成过PDF文件和CSV文件,但从来没有处理过docx和xlsx文件。这些天了解到MS Office是基于XML的。我不禁想知道是否有一些库来帮助我生成这些文件。经过一番在线搜索,结果表明确实有:Docx4j。我开始去试用这个新库看它能做些什么。

在官方网站上你可以了解到docx4j可以读取、更新、创建docx、xlsx和pptx文档。我只需要生成文档,所以我没有尝试读取和更新文档,但原理是一样的。Docx4j已经自带了不少示例程序,但我发现其中的一些例子同时展示了多样东西或者没有完全展示我需要了解的。所以有不少东西我不得不自己解决。幸运的是docx4j严格遵循Office Open XML标准,所以还不算太难。总而言这,我对这个库所提供的功能非常满意。

在这个博客中我会展示一些我试用docx4j生成docx文档时创建的例子:

一些提示

在真正示例开始之前有几处提示。首先,在使用docx创建文档的时候,看看你使用word软件创建文档时生成的xml。当你使用一个ZIP压缩工具(例如7zip)打开一个docx文件时会找到这些XML文件。看一下都是些什么样的文件以及它们包含了哪些信息。通常情况下,如果你想得到什么东西,可以通过查看Word软件自己生成了什么而从中得到很多帮助。

其次,去看一下 the Office Open Specification

现在让我们来看一些代码。

用一些文本创建文档

最简单的例子就是创建一个新word文档并向其中添加一些文本内容。

运行下面代码:

  1. WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
  2. wordMLPackage.getMainDocumentPart().addParagraphOfText("Hello Word!");
  3. wordMLPackage.save(new java.io.File("src/main/files/HelloWord1.docx"));

结果生成一个如下所示的文件:

这里发生的就是一个word文档由一个带有文档部件的包组成。Docx4j提供一个创建包的工具方法(第1行);然后你可以从这个包中获取主文档部件并向其中添加一个文本段落(第2行);最后将这个包保存(第3行)。就这么简单,当然,如果你想做不仅仅是向文档中添加一些文本内容,那么你需要做更多的工作。

添加带样式的文本

然而即使添加带样式的文本也并不需要做过多的工作,不是向文档添加一个普通的文本段落,而是只需要添加一个带样式的段落。

 
  1. WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage();
  2. wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello Word!");
  3. wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Subtitle","This is a subtitle!");
  4. wordMLPackage.save(new java.io.File("src/main/files/HelloWord2.docx"));

添加表格

当我们想添加一个表格时,情况开始变得复杂一些。直到现在我们的代码都如此简单以至于不需要拆分到多个方法中,所以我仅仅只展示了代码中相关的数行。但这个示例我们需要第二个方法,所以我会展示整个类。

 
  1. public class AddingATable {
  2. private static WordprocessingMLPackage  wordMLPackage;
  3. private static ObjectFactory factory;
  4. public static void main (String[] args) throws Docx4JException {
  5. wordMLPackage = WordprocessingMLPackage.createPackage();
  6. factory = Context.getWmlObjectFactory();
  7. Tbl table = factory.createTbl();
  8. Tr tableRow = factory.createTr();
  9. addTableCell(tableRow, "Field 1");
  10. addTableCell(tableRow, "Field 2");
  11. table.getContent().add(tableRow);
  12. wordMLPackage.getMainDocumentPart().addObject(table);
  13. wordMLPackage.save(new java.io.File("src/main/files/HelloWord4.docx"));
  14. }
  15. private static void addTableCell(Tr tableRow, String content) {
  16. Tc tableCell = factory.createTc();
  17. tableCell.getContent().add(
  18. wordMLPackage.getMainDocumentPart().createParagraphOfText(content));
  19. tableRow.getContent().add(tableCell);
  20. }
  21. }

我们再一次创建了word包,只是这一次我们将其保存为一个属性,从而我们可以在第二个方法中访问它。然后我们同样创建了一个对象工厂(ObjectFactory),这是一个帮助我们创建本例中所需要的具体对象的工具类,例如表格、表格行及表格列等具体对象。

然后我们创建了表格,创建了行并且在行中添加了两个单元格。最后我们将行添加到表格中,将表格添加到主文档部件并且将包保存。

给表格添加边框

现在我们拥有了一个表格,让我们给它添加一些边框。这需要深入研究一下表格的结构。

 
  1. public class TableWithBorders {
  2. private static WordprocessingMLPackage  wordMLPackage;
  3. private static ObjectFactory factory;
  4. public static void main (String[] args) throws Docx4JException {
  5. wordMLPackage = WordprocessingMLPackage.createPackage();
  6. factory = Context.getWmlObjectFactory();
  7. Tbl table = createTableWithContent();
  8. addBorders(table);
  9. wordMLPackage.getMainDocumentPart().addObject(table);
  10. wordMLPackage.save(new java.io.File(
  11. "src/main/files/HelloWord5.docx") );
  12. }
  13. private static void addBorders(Tbl table) {
  14. table.setTblPr(new TblPr());
  15. CTBorder border = new CTBorder();
  16. border.setColor("auto");
  17. tborder.setSz(new BigInteger("4"));
  18. border.setSpace(new BigInteger("0"));
  19. border.setVal(STBorder.SINGLE);
  20. TblBorders borders = new TblBorders();
  21. borders.setBottom(border);
  22. borders.setLeft(border);
  23. borders.setRight(border);
  24. borders.setTop(border);
  25. borders.setInsideH(border);
  26. borders.setInsideV(border);
  27. table.getTblPr().setTblBorders(borders);
  28. }
  29. private static Tbl createTableWithContent() {
  30. Tbl table = factory.createTbl();
  31. Tr tableRow = factory.createTr();
  32. addTableCell(tableRow, "Field 1");
  33. addTableCell(tableRow, "Field 2");
  34. table.getContent().add(tableRow);
  35. return table;
  36. }
  37. private static void addTableCell(Tr tableRow, String content) {
  38. Tc tableCell = factory.createTc();
  39. tableCell.getContent().add(
  40. wordMLPackage.getMainDocumentPart().
  41. createParagraphOfText(content));
  42. tableRow.getContent().add(tableCell);
  43. }
  44. }

首先我们创建了一个默认颜色(黑色)、粗细尺寸为4、间距为0的单线边框的边框组件(Border component),然后边框被应用到表格的四周以及表格内部水平和垂直的边框。随后我们将边框应用到表格;所有其它的代码与前面的示例一样。

给表格添加样式

接下来的问题--给表格添加样式让我忙活了一阵子,我们的客户想要在表格中使用粗体文本并且需要多种大小的字体。

这个示例程序有点复杂,注释和代码交织在一起。

 
  1. public class TableWithStyledContent {
  2. private static WordprocessingMLPackage  wordMLPackage;
  3. private static ObjectFactory factory;
  4. /**
  5. *  跟前面的做的一样, 我们再一次创建了一个表格, 并添加了三个单元格, 其中有两个
  6. *  单元带有样式. 在新方法中我们传进表格行, 单元格内容, 是否为粗体及字体大小作
  7. *  为参数. 你需要注意, 因为the Office Open specification规范定义这个属性是半个
  8. *  点(half-point)大小, 因此字体大小需要是你想在Word中显示大小的两倍,
  9. */
  10. public static void main (String[] args) throws Docx4JException {
  11. wordMLPackage = WordprocessingMLPackage.createPackage();
  12. factory = Context.getWmlObjectFactory();
  13. Tbl table = factory.createTbl();
  14. Tr tableRow = factory.createTr();
  15. addRegularTableCell(tableRow, "Normal text");
  16. addStyledTableCell(tableRow, "Bold text", true, null);
  17. addStyledTableCell(tableRow, "Bold large text", true, "40");
  18. table.getContent().add(tableRow);
  19. addBorders(table);
  20. wordMLPackage.getMainDocumentPart().addObject(table);
  21. wordMLPackage.save(new java.io.File("src/main/files/HelloWord6.docx") );
  22. }
  23. /**
  24. *  本方法创建单元格, 添加样式后添加到表格行中
  25. */
  26. private static void addStyledTableCell(Tr tableRow, String content,
  27. boolean bold, String fontSize) {
  28. Tc tableCell = factory.createTc();
  29. addStyling(tableCell, content, bold, fontSize);
  30. tableRow.getContent().add(tableCell);
  31. }
  32. /**
  33. *  这里我们添加实际的样式信息, 首先创建一个段落, 然后创建以单元格内容作为值的文本对象;
  34. *  第三步, 创建一个被称为运行块的对象, 它是一块或多块拥有共同属性的文本的容器, 并将文本对象添加
  35. *  到其中. 随后我们将运行块R添加到段落内容中.
  36. *  直到现在我们所做的还没有添加任何样式, 为了达到目标, 我们创建运行块属性对象并给它添加各种样式.
  37. *  这些运行块的属性随后被添加到运行块. 最后段落被添加到表格的单元格中.
  38. */
  39. private static void addStyling(Tc tableCell, String content, boolean bold, String fontSize) {
  40. P paragraph = factory.createP();
  41. Text text = factory.createText();
  42. text.setValue(content);
  43. R run = factory.createR();
  44. run.getContent().add(text);
  45. paragraph.getContent().add(run);
  46. RPr runProperties = factory.createRPr();
  47. if (bold) {
  48. addBoldStyle(runProperties);
  49. }
  50. if (fontSize != null && !fontSize.isEmpty()) {
  51. setFontSize(runProperties, fontSize);
  52. }
  53. run.setRPr(runProperties);
  54. tableCell.getContent().add(paragraph);
  55. }
  56. /**
  57. *  本方法为可运行块添加字体大小信息. 首先创建一个"半点"尺码对象, 然后设置fontSize
  58. *  参数作为该对象的值, 最后我们分别设置sz和szCs的字体大小.
  59. *  Finally we'll set the non-complex and complex script font sizes, sz and szCs respectively.
  60. */
  61. private static void setFontSize(RPr runProperties, String fontSize) {
  62. HpsMeasure size = new HpsMeasure();
  63. size.setVal(new BigInteger(fontSize));
  64. runProperties.setSz(size);
  65. runProperties.setSzCs(size);
  66. }
  67. /**
  68. *  本方法给可运行块属性添加粗体属性. BooleanDefaultTrue是设置b属性的Docx4j对象, 严格
  69. *  来说我们不需要将值设置为true, 因为这是它的默认值.
  70. */
  71. private static void addBoldStyle(RPr runProperties) {
  72. BooleanDefaultTrue b = new BooleanDefaultTrue();
  73. b.setVal(true);
  74. runProperties.setB(b);
  75. }
  76. /**
  77. *  本方法像前面例子中一样再一次创建了普通的单元格
  78. */
  79. private static void addRegularTableCell(Tr tableRow, String content) {
  80. Tc tableCell = factory.createTc();
  81. tableCell.getContent().add(
  82. wordMLPackage.getMainDocumentPart().createParagraphOfText(
  83. content));
  84. tableRow.getContent().add(tableCell);
  85. }
  86. /**
  87. *  本方法给表格添加边框
  88. */
  89. private static void addBorders(Tbl table) {
  90. table.setTblPr(new TblPr());
  91. CTBorder border = new CTBorder();
  92. border.setColor("auto");
  93. border.setSz(new BigInteger("4"));
  94. border.setSpace(new BigInteger("0"));
  95. border.setVal(STBorder.SINGLE);
  96. TblBorders borders = new TblBorders();
  97. borders.setBottom(border);
  98. borders.setLeft(border);
  99. borders.setRight(border);
  100. borders.setTop(border);
  101. borders.setInsideH(border);
  102. borders.setInsideV(border);
  103. table.getTblPr().setTblBorders(borders);
  104. }
  105. }

纵向合并单元格

有时你想要一个三列的表格,其中第一列将多行组合在一起。

 
  1. package com.zyh.sample.docx4j;
  2. public class TableWithMergedCells {
  3. private static WordprocessingMLPackage  wordMLPackage;
  4. private static ObjectFactory factory;
  5. /**
  6. *  创建一个带边框的表格并添加四个带内容的行, 然后将表格添加到文档并保存
  7. */
  8. public static void main (String[] args) throws Docx4JException {
  9. wordMLPackage = WordprocessingMLPackage.createPackage();
  10. factory = Context.getWmlObjectFactory();
  11. Tbl table = factory.createTbl();
  12. addBorders(table);
  13. addTableRowWithMergedCells("Heading 1", "Heading 1.1",
  14. "Field 1", table);
  15. addTableRowWithMergedCells(null, "Heading 1.2", "Field 2", table);
  16. addTableRowWithMergedCells("Heading 2", "Heading 2.1",
  17. "Field 3", table);
  18. addTableRowWithMergedCells(null, "Heading 2.2", "Field 4", table);
  19. wordMLPackage.getMainDocumentPart().addObject(table);
  20. wordMLPackage.save(new java.io.File(
  21. "src/main/files/HelloWord9.docx") );
  22. }
  23. /**
  24. *  本方法创建一行, 并向其中添加合并列, 然后添加再两个普通的单元格. 随后将该行添加到表格
  25. */
  26. private static void addTableRowWithMergedCells(String mergedContent,
  27. String field1Content, String field2Content, Tbl table) {
  28. Tr tableRow1 = factory.createTr();
  29. addMergedColumn(tableRow1, mergedContent);
  30. addTableCell(tableRow1, field1Content);
  31. addTableCell(tableRow1, field2Content);
  32. table.getContent().add(tableRow1);
  33. }
  34. /**
  35. *  本方法添加一个合并了其它行单元格的列单元格. 如果传进来的内容是null, 传空字符串和一个为null的合并值.
  36. */
  37. private static void addMergedColumn(Tr row, String content) {
  38. if (content == null) {
  39. addMergedCell(row, "", null);
  40. } else {
  41. addMergedCell(row, content, "restart");
  42. }
  43. }
  44. /**
  45. *  我们创建一个单元格和单元格属性对象.
  46. *  也创建了一个纵向合并对象. 如果合并值不为null, 将它设置到合并对象中. 然后将该对象添加到
  47. *  单元格属性并将属性添加到单元格中. 最后设置单元格内容并将单元格添加到行中.
  48. *
  49. *  如果合并值为'restart', 表明要开始一个新行. 如果为null, 继续按前面的行处理, 也就是合并单元格.
  50. */
  51. private static void addMergedCell(Tr row, String content, String vMergeVal) {
  52. Tc tableCell = factory.createTc();
  53. TcPr tableCellProperties = new TcPr();
  54. VMerge merge = new VMerge();
  55. if(vMergeVal != null){
  56. merge.setVal(vMergeVal);
  57. }
  58. tableCellProperties.setVMerge(merge);
  59. tableCell.setTcPr(tableCellProperties);
  60. if(content != null) {
  61. tableCell.getContent().add(
  62. wordMLPackage.getMainDocumentPart().
  63. createParagraphOfText(content));
  64. }
  65. row.getContent().add(tableCell);
  66. }
  67. /**
  68. *  本方法为给定的行添加一个单元格, 并以给定的段落作为内容
  69. */
  70. private static void addTableCell(Tr tr, String content) {
  71. Tc tc1 = factory.createTc();
  72. tc1.getContent().add(
  73. wordMLPackage.getMainDocumentPart().createParagraphOfText(content));
  74. tr.getContent().add(tc1);
  75. }
  76. /**
  77. *  本方法为表格添加边框
  78. */
  79. private static void addBorders(Tbl table) {
  80. table.setTblPr(new TblPr());
  81. CTBorder border = new CTBorder();
  82. border.setColor("auto");
  83. border.setSz(new BigInteger("4"));
  84. border.setSpace(new BigInteger("0"));
  85. border.setVal(STBorder.SINGLE);
  86. TblBorders borders = new TblBorders();
  87. borders.setBottom(border);
  88. borders.setLeft(border);
  89. borders.setRight(border);
  90. borders.setTop(border);
  91. borders.setInsideH(border);
  92. borders.setInsideV(border);
  93. table.getTblPr().setTblBorders(borders);
  94. }
  95. }

为表格设置列宽

作为本博客的最后一个示例, 我会展示怎样为表格设置列宽。

 
  1. public class SettingColumnWidthForTable {
  2. private static WordprocessingMLPackage  wordMLPackage;
  3. private static ObjectFactory factory;
  4. /**
  5. *  创建一个带边框的表格并添加一行. 然后添加两个带内容的单元格并给定宽度.
  6. */
  7. public static void main (String[] args) throws Docx4JException {
  8. wordMLPackage = WordprocessingMLPackage.createPackage();
  9. factory = Context.getWmlObjectFactory();
  10. Tbl table = factory.createTbl();
  11. addBorders(table);
  12. Tr tr = factory.createTr();
  13. addTableCellWithWidth(tr, "Field 1", 2500);
  14. addTableCellWithWidth(tr, "Field 2", 0);
  15. table.getContent().add(tr);
  16. wordMLPackage.getMainDocumentPart().addObject(table);
  17. wordMLPackage.save(new java.io.File(
  18. "src/main/files/HelloWord13.docx") );
  19. }
  20. /**
  21. *  本方法创建一个单元格并将给定的内容添加进去.
  22. *  如果给定的宽度大于0, 将这个宽度设置到单元格.
  23. *  最后, 将单元格添加到行中.
  24. */
  25. private static void addTableCellWithWidth(Tr row, String content, int width){
  26. Tc tableCell = factory.createTc();
  27. tableCell.getContent().add(
  28. wordMLPackage.getMainDocumentPart().createParagraphOfText(
  29. content));
  30. if (width > 0) {
  31. setCellWidth(tableCell, width);
  32. }
  33. row.getContent().add(tableCell);
  34. }
  35. /**
  36. *  本方法创建一个单元格属性集对象和一个表格宽度对象. 将给定的宽度设置到宽度对象然后将其添加到
  37. *  属性集对象. 最后将属性集对象设置到单元格中.
  38. */
  39. private static void setCellWidth(Tc tableCell, int width) {
  40. TcPr tableCellProperties = new TcPr();
  41. TblWidth tableWidth = new TblWidth();
  42. tableWidth.setW(BigInteger.valueOf(width));
  43. tableCellProperties.setTcW(tableWidth);
  44. tableCell.setTcPr(tableCellProperties);
  45. }
  46. /**
  47. *  本方法为表格添加边框
  48. */
  49. private static void addBorders(Tbl table) {
  50. table.setTblPr(new TblPr());
  51. CTBorder border = new CTBorder();
  52. border.setColor("auto");
  53. border.setSz(new BigInteger("4"));
  54. border.setSpace(new BigInteger("0"));
  55. border.setVal(STBorder.SINGLE);
  56. TblBorders borders = new TblBorders();
  57. borders.setBottom(border);
  58. borders.setLeft(border);
  59. borders.setRight(border);
  60. borders.setTop(border);
  61. borders.setInsideH(border);
  62. borders.setInsideV(border);
  63. table.getTblPr().setTblBorders(borders);
  64. }
  65. }

总结

在这篇文章中,我展示了如何创建一个文档、如何为其应用样式以及添加带样式的表格。以后的文章我会展示更多的关于docx文档的示例(比如添加目录、添加图片、添加页眉和页脚等)以及一些关于xlsx文档的示例。

使用Docx4j创建word文档的更多相关文章

  1. C#实现通过模板自动创建Word文档的方法

    原文地址:http://www.jb51.net/article/55332.htm   本文实例讲述了C#实现通过模板自动创建Word文档的方法,是非常实用的技巧.分享给大家供大家参考.具体实现方法 ...

  2. 在C#中创建word文档

    在下面文档中  首先引用word组件:Microsoft.Office.Interop.Word 在头文件中写上 using Word = Microsoft.Office.Interop.Word; ...

  3. Java 后台创建word 文档

    ---恢复内容开始--- Java 后台创建 word 文档 自己总结  网上查阅的文档 分享POI 教程地址:http://www.tuicool.com/articles/emqaEf6 方式一. ...

  4. [java,2017-05-04] 创建word文档

    package test; import java.text.SimpleDateFormat; import java.util.Date; import com.aspose.words.Data ...

  5. OpenXml SDK 2.0 创建Word文档 添加页、段落、页眉和页脚

    using (WordprocessingDocument objWordDocument = WordprocessingDocument.Create(@"C:\********.doc ...

  6. Python批量创建word文档(2)- 加图片和表格

    Python创建word文档,任务要求:小杨在一家公司上班,每天都需要给不同的客户发送word文档,以告知客户每日黄金价格.要求在文档开始处给出banner条,价格日期等用表格表示.最后贴上自己的联系 ...

  7. Python批量创建word文档(1)- 纯文字

    Python创建word文档,任务要求:小杨在一家公司上班,每天都需要给不同的客户发送word文档,以告知客户每日黄金价格.最后贴上自己的联系方式.代码如下: 1 ''' 2 #python根据需求新 ...

  8. C#程序通过模板自动创建Word文档

    引言:前段时间有项目要用c#生成Word格式的计算报告,通过网络查找到很多内容,但是都很凌乱,于是自己决定将具体的步骤总结整理出来,以便于更好的交流和以后相似问题可以迅速的解决! 现通过具体的示例演示 ...

  9. 使用Freemarker创建word文档

    最近做一个项目,本来是直接在网页上查看文本信息,然后给客户直接打印的,但是发现也许是浏览器还是打印机的原因,总之,有个客户打印出来的格式始终与其他的不同,没办法,最后想到了直接将数据库中的信息生成一个 ...

随机推荐

  1. 【SE】Week7 : Silver Bullet & Cathedral and Bazaar & Big Ball of Mud & Waterfall ...

    1. Silver Bullet No Silver Bullet: Essence and Accidents of Software Engineering —— 无银弹理论,出自于美国1999年 ...

  2. Daily Scrum NO.5

    工作概况 符美潇 昨日完成的工作 1.Daily Scrum.日常会议及日常工作的分配和查收. 2.变更集461代码签入,主要与视频链接爬取有关. 今日工作 1.Daily Scrum.日常会议及日常 ...

  3. Linux第一章第二章学习笔记

    第一章 Linux内核简介 1.1 Unix的历史 它是现存操作系统中最强大最优秀的系统. 设计简洁,在发布时提供原代码. 所有东西都被当做文件对待. Unix的内核和其他相关软件是用C语言编写而成的 ...

  4. linux第七章笔记

    第七章 链接 链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或拷贝)到存储器并执行.链接可以执行于编译时,也就是在源代码被翻译成机器代码时:也可以执行于加载时,也就 ...

  5. 《Linux内核分析》第四周笔记 扒开系统调用的三层皮(上)

    扒开系统调用的三层皮(上) 一.用户态.内核态和中断 库函数将系统调用封装起来. 1.什么是用户态和内核态 一般现代CPU都有几种不同的指令执行级别. 在高执行级别下,代码可以执行特权指令,访问任意的 ...

  6. 将搬家至CSDN

    emmm,感觉没利用好博客,自己也弄了一个github上面的hexo博客https://clarkkun.github.io/,但是死活传不上去内容,尴尬 ̄□ ̄||,三个博客齐头并进吧

  7. C语言入门:01.C语言概述

    一.计算机和软件常识 1.计算机运行原理 (1)硬件基本组成:硬盘.内存.CPU (2)个部件之间的运作协调(下图)

  8. 【BZOJ1966】[AHOI2005]病毒检测(动态规划)

    [BZOJ1966][AHOI2005]病毒检测(动态规划) 题面 BZOJ 洛谷 题解 我就蒯了一份代码随便改了改怎么就过了??? 从这道题目蒯的 代码: #include<iostream& ...

  9. CRM 2013发邮件的插件报错Cannot open Sql Encryption Symmetric Key because Symmetric Key password does not exist in Config DB

    解决方法: 1. 依次打开Settings->Data management –> Data Encryption 然后在上面红框里填上任意一个key即可.

  10. 洛谷 P4389 付公主的背包 解题报告

    P4389 付公主的背包 题目背景 付公主有一个可爱的背包qwq 题目描述 这个背包最多可以装\(10^5\)大小的东西 付公主有\(n\)种商品,她要准备出摊了 每种商品体积为\(V_i\),都有\ ...