[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程批处理在线视频分享
返回列表 发帖

[文本处理] [已解决]切割关卡文本的批处理代码要怎样写?

本帖最后由 pan528 于 2018-7-12 13:01 编辑

我想将关卡集的文本按每个关卡切割,并提取作者姓名、关卡文件名称、关序、关卡名称对每次个关卡重命名。
我写的代码如下,但运行后发现,命名关卡名称不可靠,有部分不能提取,不知道问题出在哪里,请高手指点:
  1. @echo off
  2. COLOR 1F
  3. set F1=%~nx1
  4. set /p F1=请输入源目录:
  5. set F2=Author
  6. echo.
  7. echo 1、按关卡切割文本(关卡较大则切割时间较长),请稍候 ...
  8. echo.
  9. if not exist %F2% md %F2%
  10. setlocal enabledelayedexpansion
  11. for /f "delims=" %%a in ('dir/s /b /on %F1%\*.*')do (
  12. for /f "delims=" %%i in (%%a)do (
  13. set m=%%i
  14. if "!m:~0,5!" == "Level" set x=!m!& echo.>%F2%\%%~na_!x:~5!.txt
  15.          if not "!m:~0,5!" == "Level" echo %%i>>%F2%\%%~na_!x:~5!.txt
  16. )
  17. )
  18. :: 将关卡序号改为四位数
  19. setlocal enabledelayedexpansion
  20. for /f "tokens=1-3 delims=_" %%i in ('dir /b /o:n %F2%\')do (
  21. set m=00000%%~nj
  22. ren "%F2%\%%i_%%j" "%%i_!m:~-5!.txt"
  23. )
  24. echo 2、加入关卡名称 ...
  25. setlocal enabledelayedexpansion
  26. for /f "delims=" %%a in ('dir /b /o %F2%\*.*')do (
  27. set n=%%a
  28. for /f "delims=" %%i in (%F2%\%%a)do (
  29. set m=%%i
  30. if "!m:~0,5!" == "Title" ren "%F2%\%%a" "!n:~0,-4!_!m:~7!.txt"
  31. )
  32. )
  33. :: 剔除空格
  34. setlocal enabledelayedexpansion
  35. for /f "delims=" %%a in ('dir/s /b /on %F2%\*.txt')do (
  36. set "var=%%~na"
  37. set var=!var:^ ^ =-!
  38. set var=!var:^ =-!
  39. set var=!var:--=-!
  40. ren "%%a" !var!.txt
  41. )
  42. echo 3、加入作者名称 ...
  43. setlocal enabledelayedexpansion
  44. for /f "delims=" %%a in ('dir/s /b /on %F2%\*.*')do (
  45. for /f "delims=" %%i in (%%a)do (
  46. set m=%%i
  47. if "!m:~0,6!" == "Author" set m=!m!&ren "%%a" "!m:~8!_%%~na.txt"
  48. )
  49. )
  50. :: 替换字符
  51. setlocal enabledelayedexpansion
  52. for /f "delims=" %%a in ('dir/s /b /on %F2%\*.txt')do (
  53. set "var=%%~na"
  54. set var=!var:^ ^+^ =,!
  55. set var=!var:^ ^+=,!
  56. set var=!var:^+^ =,!
  57. set var=!var:^+=,!
  58. set var=!var:^ ^ =-!
  59. set var=!var:^ =-!
  60. set var=!var:--=-!
  61. ren "%%a" !var!.txt
  62. )
  63. echo.
  64. echo 操作完毕!请检查命名情况,并修正!
  65. echo.
  66. echo 按任意键打开 %F2% 目录!
  67. pause>nul
  68. start %F2%
  69. goto:eof
复制代码
会出问题的关卡集:
附件: 您需要登录才可以下载或查看附件。没有帐号?注册

把文本的格式转为ansi格式试试。
你自己对比下看看出问题的是否都是
  1. Title:
  2. Author:
复制代码
这样的格式
初学BAT,非专业。代码不适当之处还望前辈们多多指点。在此表示感谢!

TOP

回复 2# xxpinqz


谢谢回帖!

我用TextForever转换,或用记事本另存为ansi格式,还是不行。

我猜想:是否一个文本中就包括了两种格式,如果是,有什么工具可彻底转换呢?

还请高手伸出援手。

TOP

文本建议用Notepad+
你这原始文本不能直接另存为ansi格式,得拐个弯:
  1. 1、新建一个文本文档b
  2. 2、用Notepad打开原始文档a,全选-复制,关闭。
  3. 3、用Notepad打开新建的文本文档b,粘贴,关闭。
  4. 4、使用b文档测试看看
复制代码
原理我也不懂哈。。
初学BAT,非专业。代码不适当之处还望前辈们多多指点。在此表示感谢!

TOP

直接告诉我们样本文件的哪一题出的错不好吗?看了半天搞不懂你说的问题在哪里

TOP

本帖最后由 狄钦dQ 于 2018-7-10 21:13 编辑

当批处理执行到“echo 2、加入关卡名称 ...”这一片段,并循环到第48个文件时,字符串会"断节"。
下图截图中提示的“文件名、目录名或卷标语法不正确”,是原来应该被双引号包含的完整文件名,不知道什么原因,后双引号直接把文件名字符串切成两半,从这个后半个引号开始的字符串将以改写模式接在行首(非插入模式),而被切开的后半段文件名就会被覆盖。
以上是我看到的现象,能力有限测试无果,直到7楼大牛点出问题关键,恍然大悟。

步骤1切割得到的90个文件,即使单独使用"2、加入关卡名称 ..."也同样有问题,问题不在第48个文件上,我也测试过在其他位置断节。
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. COLOR 1F
  4. set /a N48=0
  5. for /f "delims=" %%a in ('dir /b /on Author\*')do (
  6. set /a N48+=1
  7. echo %%a !N48!
  8. if !N48! GEQ 48 pause
  9. for /f "delims=" %%i in (.\Author\%%a) do (
  10. echo "%%i"===="我会切开第48个文件名字符串"=====
  11. )
  12. )
  13. echo ---------ok-------- & pause
  14. goto:eof
复制代码
---------------------------
最后,说个小问题,楼主给的文件,发现一不规范的标题文件,但这不是问题,改改。
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
2

评分人数

    • pan528: 认真、细心,领教了。技术 + 1
    • CrLf: 1技术 + 1

TOP

本帖最后由 WHY 于 2018-7-14 17:28 编辑

示例文本很多 Title 行结尾不是以 回车换行(\r\n)结尾,而是以 \r\r\n 结尾
开启变量延迟扩展后,在 set 赋值、截取字符时,多余的回车符 \r 被保留,文件名遇到 \r 导致重命名失败。

把 39 行改成:
  1. if "!m:~0,5!" == "Title" for /f "delims=" %%j in ("!n:~0,-4!_!m:~7!") do ren "%F2%\%%a" "%%j.txt"
复制代码
4

评分人数

TOP

本帖最后由 WHY 于 2018-7-14 20:04 编辑

其实脚本有很大的优化空间,举例:
  1. @echo off
  2. COLOR 1F
  3. set "F1=%~nx1"
  4. set /p F1=请输入源目录:
  5. set "F2=Author"
  6. if not exist "%F2%" md "%F2%"
  7. setlocal enabledelayedexpansion
  8. for /f "delims=" %%a in ('dir /b /a-d "%F1%\*.txt"') do (
  9. for /f "delims=" %%i in ('type "%F1%\%%a"') do (
  10. set "m=%%i"
  11. if "!m:~0,5!" == "Level" (
  12. set /a x=10000 + !m:~5!
  13. echo;>"%F2%\%%~na_!x:~1!.txt"
  14. ) else (
  15. echo;%%i>>"%F2%\%%~na_!x:~1!.txt"
  16. if "!m:~0,5!" == "Title" (
  17. for /f "delims=" %%j in ("!m:~7!") do set "Title=%%j"
  18. ) else if "!m:~0,6!" == "Author" (
  19. set "Author=!m:~8!"
  20. )
  21. )
  22. if defined Title if defined Author (
  23. rem 替换字符
  24. set "NewName=!Author!_%%~na_!x:~1!_!Title!"
  25. set "NewName=!NewName: + =,!"
  26. set "NewName=!NewName: +=,!"
  27. set "NewName=!NewName:+ =,!"
  28. set "NewName=!NewName:+=,!"
  29. set "NewName=!NewName:  =-!"
  30. set "NewName=!NewName: =-!"
  31. set "NewName=!NewName:--=-!"
  32. rem 重命名
  33. ren "%F2%\%%~na_!x:~1!.txt" "!NewName!.txt"
  34. set "Title="
  35. set "Author="
  36. )
  37. )
  38. )
  39. pause & exit
复制代码
test.js
  1. var srcDir = '.';      //源目录
  2. var dstDir = 'Author'; //目标目录
  3. var fso = new ActiveXObject('Scripting.FileSystemObject');
  4. if(!fso.FolderExists(dstDir)) fso.CreateFolder(dstDir);  //创建文件夹
  5. var objFolder = fso.GetFolder(srcDir);
  6. var e = new Enumerator(objFolder.Files);
  7. for(; !e.atEnd(); e.moveNext()){
  8.     var f = e.item();
  9.     if(!/\.txt$/i.test(f.Name)) continue;  //忽略非txt后缀文件
  10.     var baseName = f.Name.replace(/\.txt$/i,'');
  11.     var txt = fso.OpenTextFile(f, 1).ReadAll();  //打开、读取文本
  12.     var arr = txt.split(/[\r\n]+Level(?=\d+)/);  //分割数组
  13.     for(var i=0; i<arr.length; i++) writeToFile(arr[i], baseName);
  14. }
  15. function writeToFile(str, baseName){
  16.     var num = str.match(/^\d+/);    //第一行数字赋值给num
  17.     var author = str.match(/^Author:[^\r\n]+/m);
  18.     var title = str.match(/^Title:[^\r\n]+/m);
  19.     if(num && author && title){
  20.         num = (10000 + num * 1 + '').substr(1);
  21.         var newName = author + '_' + baseName + '_' + num + '_' + title;
  22.         //删除非法文件名字符,删除 Author: 和 Title:字符,替换字符
  23.         newName = newName.replace(/(Author|Title):\s*|[\\/|<>?*":]/g, '').replace(/\s*\+\s*/g, ',').replace(/\s+/g, '-');
  24.             
  25.         str = str.replace(/^[^\r\n]+/, ''); //删除第一行(数字行)
  26.         fso.CreateTextFile(dstDir + '\\' + newName + '.txt', true).writeLine(str); //写入txt文件
  27.     }
  28. }
  29. WSH.Echo('Done');
复制代码
2

评分人数

    • pan528: 谢谢,代码太精彩了!技术 + 1
    • CrLf: 1技术 + 1

TOP

本帖最后由 狄钦dQ 于 2018-7-10 21:31 编辑
示例文本很多 Title 行结尾不是以 回车换行(\r\n)结尾,而是以 \r\r\n 结尾
开启变量延长扩展后,多余的 \ ...
WHY 发表于 2018-7-10 11:12

感谢7楼大佬的点拨,现在我来对@WHY的回答做点补充。首先我们用Winhex工具打开楼主的文本附件,就能看到了文件最真实的样子:

在ASCII标准码表中,换行符(缩写:LF)对应16进制为0A,转义符/n;回车符\r(缩写:CR)对应16进制为0D,转义符/r。从上图观察我们能明显看到:异常的Tile行末多了个0D即回车符\r,从而确定问题的症结。
检查了楼主的文本,这种问题总共出现在7处位置,分别是#18,#86,#123,#154,#150,#169,#188。
那为何用notepad查看文本,并没有发现异常的Title与其他行有什么区别,也都没有换行?
答:对于windows 下标准文本,它只认"\r\n"为换行,单独"\r"或"\n"就直接忽略。但是在其他编辑器就不一定啦,例如用notepad++就能看到异常的Tile换行了。

知道问题关键了,就要说下解决办法:
方法一:8楼@WHY 的示例代码,这里通过取巧的方法避开"字符串断节"——避免在!m:~7!后再继续添加任何字符(方法不治本),这样就能避免字符串在行首改写。
方法二:规范你的数据文本(不干净的数据真的坑),使用notepad++的查找替换(扩展模式),将"\r\r\n"替换成"\r\n"即可,原批处理命令都不用修改了。
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
2

评分人数

TOP

回复 9# 狄钦dQ


    补充一下,其实好多编辑器带了类似的功能,是可以显示空白字符的。。nopad++  点view 然后点 show symbol ,选上show end of line。就可以直接看到\r、\n符了。
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

有汉化的notepad++对应估计是查看,显示符号里设置吧。
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

notepad++ 在左下角可以切换换行风格,先切成linux再切回windows保存就可以了。
去学去写去用才有进步。安装python3代码存为xx.py 双击运行或右键用IDLE打开按F5运行

TOP

回复 12# codegay


    学习了,谢谢:handshake

TOP

Notepad++菜单栏有个按钮可以“显示所有字符”,也能看到CRLF
【扫描二维码捐助论坛的朋友请留言注明论坛账号】http://bbs.bathome.net/thread-10403-1-1.html
【批处理在线视频分享】http://bbs.bathome.net/thread-31727-1-1.html
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html

TOP

回复 14# Batcher


    WTF????

TOP

返回列表