Board logo

标题: [文本处理] 【已解决】求助批处理从一堆XML中提取关键词写到csv中 [打印本页]

作者: zhengwei007    时间: 2024-9-19 09:41     标题: 【已解决】求助批处理从一堆XML中提取关键词写到csv中

本帖最后由 zhengwei007 于 2024-9-21 08:52 编辑

我有一堆文件名乱起的XML文件,文件中内容大致为以下内容:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../xsd/skillTrees.xsd">
  3. <!-- Arcana Lord -->
  4. <skillTree type="classSkillTree" classId="96" parentClassId="14">
  5. <!-- Confirmed CT2.5 and Updated to H5 -->
  6. <skill skillName="Wisdom" skillId="328" skillLevel="1" getLevel="76" levelUpSp="6250000" learnedByNpc="true" />
  7. <!-- Forgotten Scrolls -->
  8. <skill skillName="Spirit of the Cat" skillId="929" skillLevel="1" getLevel="83" learnedByFS="true" />
  9. <!-- HighFive new -->
  10. <skill skillName="Dimension Spiral" skillId="1558" skillLevel="15" getLevel="76" levelUpSp="6250000" learnedByNpc="true" />
  11. </skillTree>
  12. </list>
复制代码
我需要用一个批处理,把所有文件中skill skillName等字段全部单独挑出来,每个字段前都加上classId="96",我写下执行完的结果:
  1. classID skillName skillId skillLevel getLevel levelUpSp
  2. 96 Wisdom 328 1 76 12500000
  3. 96 Spirit of the Cat 929 1 83
  4. 96 Dimension Spiral 1558 15 76 6250000
复制代码

作者: qixiaobin0715    时间: 2024-9-19 12:52

本帖最后由 qixiaobin0715 于 2024-9-19 13:35 编辑

classID与其它字段不在一行,并且它们之间可能会有其它无效行的干扰(虽然也进行了粗略筛选),调试时稍微复杂一些:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set BoxHead=classId skillName skillId skillLevel getLevel levelUpSp
  4. for %%i in (%BoxHead%) do set %%i=true
  5. echo,%BoxHead%
  6. for /f "delims=" %%i in ('findstr "skill" *.xml') do (
  7.     set n=
  8.     set NewLine=!classId!
  9.     for %%j in (%%i) do (
  10.         if /i "!str!"=="classId" (
  11.             set NewLine=%%~j
  12.             set classId=%%~j
  13.         ) else if defined !str! (
  14.             set NewLine=!NewLine! %%~j
  15.             set n=1
  16.         )
  17.         set "str=%%~j"
  18.     )
  19.     if defined n (
  20.         echo,!NewLine!
  21.         set NewLine=
  22.     )
  23. )
  24. pause
复制代码

作者: ppll2030    时间: 2024-9-19 14:41

回复 1# zhengwei007


    代码保存为bat文件,跟xml放一起,运行后会生成与xml同名的csv文件。
  1. @echo off &setlocal enabledelayedexpansion
  2. for /f "delims=" %%i in ('dir /b /a-d *.xml') do (
  3. (set "v="&echo classID, skillName, skillId, skillLevel, getLevel, levelUpSp
  4. for /f tokens^=1-10^ delims^=^ ^=^<^" %%a in (' findstr "=" "%%i" ') do (
  5. if /i "%%a"=="skillTree type" (set "v1=%%d")
  6. if /i "%%a"=="skill skillName" (
  7. set "v=!v1!, %%b, %%d, %%f, %%h"
  8. if /i "%%j" neq "true" (echo !v!, %%j) else echo !v!
  9. )
  10. ))>%%~ni.csv
  11. )
  12. pause
复制代码

作者: qixiaobin0715    时间: 2024-9-20 09:34

优化2楼代码,并把分隔符修改为逗号:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set BoxHead=classId,skillName,skillId,skillLevel,getLevel,levelUpSp
  4. for %%i in (%BoxHead%) do set %%i=true
  5. (echo,%BoxHead%
  6. for /f "delims=" %%i in ('findstr "skill" *.xml') do (
  7.     for %%j in (%%i) do (
  8.         if /i "!str!"=="classId" (
  9.             set classId=%%~j
  10.         ) else if defined !str! (
  11.             set NewLine=!NewLine!,%%~j
  12.         )
  13.         set "str=%%~j"
  14.     )
  15.     if defined NewLine (
  16.         echo,!classId!!NewLine!
  17.         set NewLine=
  18.     )
  19. ))>out.csv
  20. pause
复制代码

作者: aloha20200628    时间: 2024-9-20 11:13

本帖最后由 aloha20200628 于 2024-9-20 11:15 编辑

回复 1# zhengwei007

当前目录中的所有 *.xml 目标文件根据楼主提取要求及其输出格式汇总为一个结果文件 all.csv
  1. @echo off &(echo,classID skillName skillId skillLevel getLevel levelUpSp
  2. for %%F in (*.xml) do for /f tokens^=1-10^delims^=^" %%a in ('findstr /ic:" skillname=" "%%F" ') do (
  3.     set/p="96 %%b %%d %%f %%h"<nul
  4.     if /i "%%j" neq "true" (echo, %%j) else (echo,)
  5. ))>all.csv
  6. pause&exit/b
复制代码

作者: qixiaobin0715    时间: 2024-9-20 11:44

本帖最后由 qixiaobin0715 于 2024-9-20 11:51 编辑

实际上上面代码考虑的都不是太全面,看到楼主的示范文本,好像数据有缺失的情况,后面数据缺失问题不大,如果中间缺上一个,比如skillLevel的数据缺失,就会出问题,顺序如果不同也会出问题,那样的话,数据上下就会错位。还是要想一想别的办法。
作者: czjt1234    时间: 2024-9-20 12:36

建议把 skillName 放在最后一列,否则不能对齐,要用英文的逗号作分隔符输出 csv 文件
作者: 77七    时间: 2024-9-20 12:52

数据还是很规整的,只是写起来比较啰嗦...
  1. @echo off
  2. cd /d "%~dp0"
  3. set list=classId skillName skillId skillLevel getLevel levelUpSp
  4. set "_list=%list:* =%"
  5. (
  6. echo %list: =,%
  7. setlocal enabledelayedexpansion
  8. for /f "tokens=1* delims=:" %%i in ('findstr /i "%list: == %" *.xml') do (
  9. set str=%%j
  10. set "str=!str:/=!"
  11. set "str=!str:<=!"
  12. set "str=!str:>=!"
  13. for %%a in (%_list%) do (
  14. set _%%a=,
  15. )
  16. set _str=
  17. for %%a in (!str!) do (
  18. if "%%a" equ "%%~a" (
  19. set b=%%a
  20. ) else (
  21. set _!b!=%%a
  22. )
  23. )
  24. for %%a in (%_list%) do (
  25. set _str=!_str!,!_%%a!
  26. )
  27. if "!_str:,=!" neq "" (
  28. echo !_classId!,!_str:~1!
  29. )
  30. )
  31. endlocal
  32. ) > out.csv
  33. pause
  34. exit
复制代码

作者: qixiaobin0715    时间: 2024-9-20 13:09

本帖最后由 qixiaobin0715 于 2024-9-20 13:20 编辑

兼容中间数据缺失及数据顺序不同的处理思路有了,总觉得楼主一直在看热闹。估计楼主的文件不存在我说的情况,上面大家的代码已足够处理数据,算了,不费脑子了。
作者: zhengwei007    时间: 2024-9-21 08:52

兼容中间数据缺失及数据顺序不同的处理思路有了,总觉得楼主一直在看热闹。估计楼主的文件不存在我说的情况 ...
qixiaobin0715 发表于 2024-9-20 13:09


为什么这么说呢?今天周末我才来看下,楼上的已经实现了我要的功能,大家都很厉害。
作者: qixiaobin0715    时间: 2024-9-22 08:46

本帖最后由 qixiaobin0715 于 2024-9-22 08:57 编辑

在不在线,好像大家都能看到,虽然说显示有所滞后。多说没用,算我没说。
作者: delab-1    时间: 2024-9-25 20:17

本帖最后由 delab-1 于 2024-9-25 20:37 编辑

回复 4# qixiaobin0715


    大神,可否较为清晰地帮助解释一下命令过程,没有看太懂
例如,1)在命令中,if /i "!str!=="classid" (, set str=%%~j是出现在后面程序中的,这样写可以吗?
         2) set Newline=!Newline!,%%j的作用是什么,为何要在在%%j之前放上!Newline!
         3) 没有搞清楚!str!是如何在取值上如何循环的,因为仅仅看到一个 "!str!=="classid", 后面如何和定义的%BoxHead%中的指标匹配上的。

    多谢大神解释哈
作者: qixiaobin0715    时间: 2024-9-26 09:11

本帖最后由 qixiaobin0715 于 2024-9-26 09:52 编辑

回复 12# delab-1
还是以实例自己来观察的好。
1.把代码第7行的for循环摘出来,改造成易于理解的形式。在下面代码中,我需要提取b的值“2”,应当如何处理呢?可以在最后面设置变量str,使之延后显示,也就是说当变量str的值等于“b”时,下一个循环中,%%~i的值就是"2",示范代码如下:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for %%i in (a="1" b="2" c="3") do (
  4.     echo,!str!
  5.     echo,%%~i
  6.     if /i "!str!"=="b" (
  7.         set b=%%~i
  8.         echo,*********  b=%%~i
  9.     )
  10.     echo,---------------------
  11.     echo,
  12.     set "str=%%~i"
  13. )
  14. pause
复制代码
要显示的内容我都做了特殊标记。
2.这个问题你需要补习一下变量设置方面的知识,还是举一个例子来自己观察一下吧:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /l %%i in (1,1,9) do (
  4.     set str=!str!,%%i
  5.     echo,!str!
  6. )
  7. pause
复制代码
请观察每次循环,变量str值的变化情况。
3.把前面2个问题搞清楚了,这个问题仔细想一想,也就差不多能够想通了。


关于第1条所说的“延后显示”的问题,这里也给出一个实例代码,每组显示中,前一行 为变量重新设置前的值,后一行 为变量重新设置后的值,看看是否是错位显示:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /l %%i in (1,1,5) do (
  4.     echo,!str!
  5.     set str=%%i
  6.     echo,!str!
  7.     echo,-------------
  8.     echo,
  9. )
  10. pause
复制代码

作者: delab-1    时间: 2024-9-26 09:42

回复 13# qixiaobin0715


   太感谢了,马上学习
作者: qixiaobin0715    时间: 2024-9-26 10:15

下面代码更通用一些,适合于出现数据部分缺失及顺序不同的情况,而得到的结果中数据也不会错位:
  1. @echo off
  2. set BoxHead=classId,skillName,skillId,skillLevel,getLevel,levelUpSp
  3. for %%i in (%BoxHead%) do set _%%i=true
  4. echo,%BoxHead%
  5. for /f "tokens=2* delims=: " %%i in ('findstr /i "<skill" *.xml') do (
  6.     if /i "%%i"=="<skillTree" (
  7.         for %%k in (%%j) do (
  8.             if defined n set "classId=%%~k"&set n=
  9.             if "%%k"=="classId" set n=1
  10.         )
  11.     ) else (
  12.         setlocal enabledelayedexpansion
  13.         for %%k in (%%j) do (
  14.             if defined _!str! set !str!=%%~k&set m=1
  15.             set str=%%k
  16.         )
  17.         if defined m echo,!classId!,!skillName!,!skillId!,!skillLevel!,!getLevel!,!levelUpSp!&set m=
  18.         endlocal
  19.     )
  20. )
  21. pause
复制代码

作者: aloha20200628    时间: 2024-9-26 12:11

本帖最后由 aloha20200628 于 2024-9-26 12:13 编辑

去年至今已有但多帖是从网页数据中提取指定字段的题型,本来可以用 vbs/jscript/powershell 先将网页经过 xml 格式转换工具处理,再按字段名批量提取,但此法要求源网页格式须严格符合 xml 规范,故对大量没有严格‘守法’的网页容易出现兼容性问题,对此还真不如 findstr+for/f 组合版来的简明痛快,即先用 findstr 小正则‘粗洗’网页,再用 for/f ‘精切’ 字段,findstr 的正则匹配度越高,留给批处的处理量就越少,for/f 分割器的切口越准,提取目标字段的效率越高 ... 当然此法也有短处,一旦遭遇数据行长度超过8k字符量的实况就会‘罢工’。
作者: delab-1    时间: 2024-9-27 20:57

回复 15# qixiaobin0715


    非常感谢,在学习测试中,太感谢啦




欢迎光临 批处理之家 (http://bbs.bathome.net/) Powered by Discuz! 7.2