{"id":18737474,"url":"https://github.com/6tail/dp2","last_synced_at":"2025-07-22T10:37:30.646Z","repository":{"id":43221574,"uuid":"247664021","full_name":"6tail/dp2","owner":"6tail","description":"a common data parser tool, support reading and writing xls, xlsx, doc, docx, csv.一个通用的表格数据读写工具，支持xls、xlsx、doc、docx、csv格式。","archived":false,"fork":false,"pushed_at":"2025-04-10T14:13:57.000Z","size":162,"stargazers_count":1,"open_issues_count":3,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-20T02:50:39.820Z","etag":null,"topics":["csv","doc","docx","java","xls","xlsx"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/6tail.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-16T09:43:15.000Z","updated_at":"2022-01-29T11:32:31.000Z","dependencies_parsed_at":"2025-05-20T02:52:12.969Z","dependency_job_id":null,"html_url":"https://github.com/6tail/dp2","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/6tail/dp2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/6tail%2Fdp2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/6tail%2Fdp2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/6tail%2Fdp2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/6tail%2Fdp2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/6tail","download_url":"https://codeload.github.com/6tail/dp2/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/6tail%2Fdp2/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266477152,"owners_count":23935390,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csv","doc","docx","java","xls","xlsx"],"created_at":"2024-11-07T15:25:24.555Z","updated_at":"2025-07-22T10:37:30.617Z","avatar_url":"https://github.com/6tail.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dp2  [![License](https://img.shields.io/badge/license-MIT-4EB1BA.svg?style=flat-square)](https://github.com/6tail/dp2/blob/master/LICENSE)\n\n一个通用的表格数据读写工具，支持xls、xlsx、doc、docx、csv格式。\n\n\u003e 支持在较低内存占用下完成超大xls和xlsx文件的解析。\n\n\u003e 支持直接生成新文件，也支持在模板文件中修改单元格数据并生成新的文件。\n\n\u003e 支持java1.5及以上版本。java1.5需额外引入javax包，否则解析xlsx会报错。\n\n\u003e xls和xlsx文件仅解析第一个Sheet。\n\n\u003e doc和docx文件仅解析第一个表格。\n\n## 使用\n\n通过maven引入或者[点此下载](https://github.com/6tail/dp2/releases)相应的jar。\n\n\n```xml\n\u003cdependency\u003e\n  \u003cgroupId\u003ecn.6tail\u003c/groupId\u003e\n  \u003cartifactId\u003edp2\u003c/artifactId\u003e\n  \u003cversion\u003e1.0.3\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## 示例\n\n### 待解析excel内容\n\n\u003ctable\u003e\n  \u003ctr\u003e\n  \u003cth\u003e\u003c/th\u003e\n  \u003cth\u003eA\u003c/th\u003e\n  \u003cth\u003eB\u003c/th\u003e\n  \u003cth\u003eC\u003c/th\u003e\n  \u003cth\u003eD\u003c/th\u003e\n  \u003cth\u003eE\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctd\u003e1\u003c/td\u003e\n  \u003ctd\u003e序号\u003c/td\u003e\n  \u003ctd\u003e姓名\u003c/td\u003e\n  \u003ctd\u003e性别\u003c/td\u003e\n  \u003ctd\u003e年龄\u003c/td\u003e\n  \u003ctd\u003e民族\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctd\u003e2\u003c/td\u003e\n  \u003ctd\u003e1\u003c/td\u003e\n  \u003ctd\u003e张三\u003c/td\u003e\n  \u003ctd\u003e男\u003c/td\u003e\n  \u003ctd\u003e20\u003c/td\u003e\n  \u003ctd\u003e汉族\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctd\u003e3\u003c/td\u003e\n  \u003ctd\u003e2\u003c/td\u003e\n  \u003ctd\u003e李四\u003c/td\u003e\n  \u003ctd\u003e女\u003c/td\u003e\n  \u003ctd\u003e18\u003c/td\u003e\n  \u003ctd\u003e汉族\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctd\u003e4\u003c/td\u003e\n  \u003ctd\u003e3\u003c/td\u003e\n  \u003ctd\u003e王二\u003c/td\u003e\n  \u003ctd\u003e男\u003c/td\u003e\n  \u003ctd\u003e30\u003c/td\u003e\n  \u003ctd\u003e满族\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### 自动读取示例\n\n    // 待解析文件\n    File file = new File(\"template.xls\");\n     \n    // 自动获取标记\n    List\u003cMarker\u003e markers = SingleLineRepeatMarkerDetector.detect(file);\n     \n    // 通过工厂获取解析器接口\n    IParser parser = ParserFactory.getParser(file);\n     \n    // 获取节点读取器\n    INodeReader reader = parser.read(markers);\n     \n    // 遍历所有找到的节点\n    while (reader.hasNext()) {\n      INode node = reader.next();\n       \n      // 自动识别的标题以head作为标记名称，以body作为数据标记名称\n      if (\"head\".equals(node.getMarker().getName())) {\n        // 表头不处理\n      } else if (\"body\".equals(node.getMarker().getName())) {\n        for (INode child : node.getChildren()) {\n          // 自动识别的以列下标（从0开始）作为子项标记名称\n          // System.out.print(child.getMarker().getName() + \"=\");\n          System.out.print(child.getValue());\n          System.out.print(\"\\t\");\n        }\n        System.out.println(\"_____________________________________\");\n      }\n    }\n\n### 手动读取示例\n\n    // 待解析文件\n    File file = new File(\"template.xls\");\n     \n    // 定义待解析的标记列表\n    List\u003cMarker\u003e markers = new ArrayList\u003cMarker\u003e();\n     \n    // 标题只有一行，直接使用Marker，位于行0列0（如果不解析标题，则可以不定义）\n    Marker markerHead = new Marker(\"标题\", 0, 0);\n     \n    // 设置宽度为5列（高度默认为一行不用设置了）\n    markerHead.setWidth(5);\n     \n    // 添加需要取的标题标记及位于父区域内的坐标\n    markerHead.addChild(new Marker(\"序号\", 0, 0));\n    markerHead.addChild(new Marker(\"姓名\", 0, 1));\n    markerHead.addChild(new Marker(\"性别\", 0, 2));\n    markerHead.addChild(new Marker(\"年龄\", 0, 3));\n    markerHead.addChild(new Marker(\"民族\", 0, 4));\n     \n    // 数据区域为重复性数据，使用RepeatedMarker，位于行1列0\n    Marker markerBody = new RepeatedMarker(\"数据\", 1, 0);\n     \n    // 设置宽度为5列（高度默认为一行不用设置了）\n    markerBody.setWidth(5);\n     \n    // 添加需要取的数据标记及位于父区域内的坐标\n    markerBody.addChild(new Marker(\"序号\", 0, 0));\n    markerBody.addChild(new Marker(\"姓名\", 0, 1));\n    markerBody.addChild(new Marker(\"性别\", 0, 2));\n    markerBody.addChild(new Marker(\"年龄\", 0, 3));\n    markerBody.addChild(new Marker(\"民族\", 0, 4));\n     \n    // 添加要解析的标记\n    markers.add(markerHead);\n    markers.add(markerBody);\n     \n    // 通过工厂获取解析器接口\n    IParser parser = ParserFactory.getParser(file);\n     \n    // 获取节点读取器\n    INodeReader reader = parser.read(markers);\n     \n    // 遍历所有找到的节点\n    while (reader.hasNext()) {\n      INode node = reader.next();\n       \n      if (\"标题\".equals(node.getMarker().getName())) {\n        // 表头不处理\n      } else if (\"数据\".equals(node.getMarker().getName())) {\n        for (INode child : node.getChildren()) {\n          // 子项标记名称为前面定义的标记名称，如序号、姓名等\n          // System.out.print(child.getMarker().getName() + \"=\");\n          System.out.print(child.getValue());\n          System.out.print(\"\\t\");\n        }\n        System.out.println(\"_____________________________________\");\n      }\n    }\n\n### 输出结果\n\n    1  张三  男  20  汉族\n    _____________________________________\n    2  李四  女  18  汉族\n    _____________________________________\n    3  王二  男  30  满族\n    _____________________________________\n\n\n### 生成文件示例\n\n    // 定义标记列表\n    List\u003cMarker\u003e markers = new ArrayList\u003cMarker\u003e();\n     \n    // 标题只有一行，使用Marker，位于第0行第0列\n    Marker markerHead = new Marker(\"标题\", 0, 0);\n     \n    // 设置宽度为2列\n    markerHead.setWidth(2);\n     \n    // 添加需要取的标题标记及位于父区域内的坐标\n    markerHead.addChild(new Marker(\"sn\", 0, 0));\n    markerHead.addChild(new Marker(\"name\", 0, 1));\n     \n    // 数据区域为重复性数据，使用RepeatedMarker，位于第1行第0列\n    Marker markerBody = new RepeatedMarker(\"数据\", 1, 0);\n     \n    // 设置宽度为2列\n    markerBody.setWidth(2);\n     \n    // 添加需要取的数据标记及位于父区域内的坐标\n    markerBody.addChild(new Marker(\"序号\", 0, 0));\n    markerBody.addChild(new Marker(\"姓名\", 0, 1));\n     \n    // 添加标记\n    markers.add(markerHead);\n    markers.add(markerBody);\n     \n    // 通过工厂获取xls解析接口\n    IParser parser = ParserFactory.getParser(\"xls\");\n     \n    // 获取节点写入接口\n    INodeWriter writer = parser.write(markers);\n     \n    // 写标题\n    Node head = new Node(\"标题\");\n    head.addChild(new Node(\"sn\", \"序号\"));\n    head.addChild(new Node(\"name\", \"姓名\"));\n    writer.add(head);\n     \n    // 写数据\n    Node body = new Node(\"数据\");\n    body.addChild(new Node(\"序号\", \"1\"));\n    body.addChild(new Node(\"姓名\", \"张三\"));\n    writer.add(body);\n     \n    // 再写一行数据\n    body = new Node(\"数据\");\n    body.addChild(new Node(\"序号\", \"2\"));\n    body.addChild(new Node(\"姓名\", \"李四\"));\n    writer.add(body);\n     \n    // 待生成文件\n    File file = new File(\"template.xls\");\n     \n    // 输出\n    writer.save(file);\n\n## 解析机制\n\n在一个表格中，指定横坐标、纵坐标、宽度（多少列），高度（多少行），你就能圈出一个区域。给这个区域贴上标签，你就能很容易的从表格中找到你感兴趣的内容，这个标签，也就是标记（Marker）。\n\n标记可大可小，大到整个Sheet（当然这样没有实际意义），小到一个单元格。\n\n标记支持层级，一个大的标记下，可以包含多个小的子标记（子标记的坐标以父标记为基准），这样就可以从大到小逐步锁定目标。\n\n遍历读取的时候，仅遍历顶级的标记，子标记内容通过getChildren方法获取。\n\n### 单一标记\n\n单一标记(Marker)为不重复出现的标记。\n\n以下图为例，通过\"姓名\"标记可直接读取到\"六特尔\"，通过\"email\"标记可直接读取到\"6tail@6tail.cn\"，也可修改对应的值。\n\n![单一标记示例](https://github.com/6tail/dp2/raw/master/samples/dp2-0.png)\n\n### 重复标记\n\n重复标记(RepeatedMarker)为重复出现的标记。\n\n以下图为例，每一行数据为一个大的标记（取名为\"名单\"，按行重复出现，因不确定有多少行数据，因此重复标记的坐标以第一次出现的坐标为准），大标记中包含两个子标记（\"姓名\"和\"性别\"）。\n\n![重复标记示例](https://github.com/6tail/dp2/raw/master/samples/dp2-1.png)\n\n### 组合标记\n\n无论多复杂的表格，都可以拆分为单一标记与重复标记的组合，标记表格的方式也可以五花八门，你选择的标记方式将决定解析的效率。\n\n![复杂标记示例](https://github.com/6tail/dp2/raw/master/samples/dp2-2.png)\n\n以上图这个表格为例，如果我们需要读取主题和名单，可定义两个顶级标记，\"主题\"标记位于行1列1，\"名单\"标记位于行4列1，宽2高3。\n\n其中\"名单\"中包含多个重复标记\"人员\"，位于行0列0，宽2高1。\n\n\"人员\"标记又包含\"姓名\"标记（位于行0列0，宽1高1）和\"性别\"标记（位于行0列1，宽1高1）。\n\n## 注意\n\n坐标均以父标记为基准，从0开始计。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F6tail%2Fdp2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F6tail%2Fdp2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F6tail%2Fdp2/lists"}