Board logo

标题: [文本处理] [已解决]空格补位,把输出内容对齐的批处理 [打印本页]

作者: shenlong    时间: 2024-7-17 20:35     标题: [已解决]空格补位,把输出内容对齐的批处理

a.txt 内容
aa  1232   米
bbbbb  66  米
想要把内容补空格后输出到 b.txt
aa          1232    米
bbbbb     66       米
以下我测试的代码,不能达到预期效果,请各位围观帮忙,先行谢过
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. set "k=                    "
  4. (for /f "tokens=1-4  delims=  " %%a in (a.txt) do (
  5.     set "n1=%%a" && set "n2=%%b" && set "n3=%%c" && set "n4=%%d"
  6.     set a=!n1!%k% && set b=!n2!%k%  && set  c=!n3!%k%  && set  d=!n4!%k%
  7.     echo.  !a:~0,15! !b:~0,15! !c:~0,15! !d:~0,15!
  8. )) > b.txt
  9. endlocal
复制代码

作者: ppll2030    时间: 2024-7-17 21:10

绝对对齐是做不到的。你把例子b.txt的内容复制到txt看看就明白了。
要绝对,那就导出为csv表格形式吧。那是绝对整齐
作者: shenlong    时间: 2024-7-18 07:41

回复 2# ppll2030


    多谢,实在不行了就换其它方法了。
作者: qixiaobin0715    时间: 2024-7-18 08:56

本帖最后由 qixiaobin0715 于 2024-7-18 09:01 编辑
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. for /f "tokens=1-2*" %%i in (a.txt) do (
  4.     set f1=%%i                    
  5.     set f2=%%j                    
  6.     echo,!f1:~,20!!f2:~,20!%%k
  7. )
  8. pause
复制代码

作者: shenlong    时间: 2024-7-18 09:27

回复 4# qixiaobin0715

谢谢,还是没有达到预期效果,a.txt中 最左侧的 aa   bbbbb   长度是随机和不是固定的长度。
作者: qixiaobin0715    时间: 2024-7-18 09:32

回复 5# shenlong
没搞明白你所谓的“预期效果”是指什么?
作者: aloha20200628    时间: 2024-7-18 09:39

本帖最后由 aloha20200628 于 2024-7-18 10:06 编辑

回复 1# shenlong

文本行的格式化输出,须确定最大列宽即最大字段长度。简单处理是由楼主预设一个最大列宽值,否则就要实时遍历整个文本文件获取其中最大字段长度...
给一个批处版本,代码如下》获取文本文件中的最大字段长度,假设字段分隔符是批处默认的空格、逗号、分号、等号和制表符。
  1. @echo off &setlocal enabledelayedexpansion &set "maxZ=0"
  2. for /f "delims=" %%a in (1.txt) do for %%s in (%%a) do (
  3. set/p="%%s"<nul>0.0
  4. for %%b in (0.0) do if %%~zb gtr !maxZ! set "maxZ=%%~zb"
  5. )
  6. echo,!maxZ!
  7. del/q "0.0"&endlocal&pause&exit/b
复制代码

作者: qixiaobin0715    时间: 2024-7-18 10:04

只能这样勉为其难了:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. type a.txt>temp
  4. echo,a>>temp
  5. set /a n=0,y=0
  6. for /f "delims=:" %%i in ('findstr /o .* temp') do (
  7.     set /a x=%%i-y
  8.     if !n! lss !x! set /a n=x
  9.     set y=%%i
  10. )
  11. del temp
  12. for /l %%i in (1,1,!n!) do set BS= !BS!
  13. for /f "tokens=1-2*" %%i in (a.txt) do (
  14.     set f1=%%i!BS!
  15.     set f2=%%j!BS!
  16.     echo,!f1:~,%n%!!f2:~,%n%!%%k
  17. )
  18. pause
复制代码

作者: aloha20200628    时间: 2024-7-18 11:13

本帖最后由 aloha20200628 于 2024-7-18 12:08 编辑

回复 1# shenlong
  1. @echo off &setlocal enabledelayedexpansion &set "maxZ=0"
  2. for /f "delims=" %%a in (1.txt) do for %%s in (%%a) do (
  3. set "s=%%s" & (call :_strLen s z)
  4. if !z! gtr !maxZ! set "maxZ=!z!"
  5. )
  6. set "ms=" &for /l %%n in (1,1,!maxZ!) do set "ms=!ms! "
  7. endlocal &set "maxZ=%maxZ%"&set "ms=%ms%"
  8. setlocal enabledelayedexpansion
  9. for /f "delims=" %%a in (1.txt) do (
  10. set "s="
  11. for %%s in (%%a) do (set "_s=%%s"&set "_s=!_s!%ms%"&set "s=!s! !_s:~0,%maxZ%!")
  12. echo,!s:~1!
  13. )
  14. endlocal&pause&exit/b
  15. :_strLen
  16.    set "_str=_!%~1!" &set "_Len=0"
  17.    for %%n in (64 32 16 8 4 2 1) do (
  18.       if "!_str:~%%n,1!" neq "" (set/a "_Len+=%%n"&set "_str=!_str:~%%n!")
  19.    )
  20.    set "%~2=!_Len!" &exit/b
复制代码

特此备注》重写了原代码,改用批处 ‘原装’ 的字符串长度计算方法,再请出制表符 ‘显灵’,似乎消除了以前格式化输出时中英文字符混合字段的占位问题...

作者: shenlong    时间: 2024-7-18 11:19

回复 8# qixiaobin0715


    多谢,效果可以,就用这个了。
作者: shenlong    时间: 2024-7-18 11:21

回复 7# aloha20200628


    多谢帮助。
作者: 娜美    时间: 2024-7-18 11:22

字节不同 要对齐  似乎只能改用制表符
  1. awk  -v OFS="\t"  '{print $1,$2,$3}' a.txt
复制代码

作者: qixiaobin0715    时间: 2024-7-18 11:23

本帖最后由 qixiaobin0715 于 2024-7-18 12:54 编辑

这样可能要美观一些:
  1. @echo off
  2. for /f "tokens=1-3" %%i in (a.txt) do (
  3.     echo,%%i>>temp1
  4.     echo,%%j>>temp2
  5. )
  6. set /a n1=n2=0,y1=y2=0
  7. setlocal enabledelayedexpansion
  8. for %%a in (1 2) do (
  9.     echo,a>>temp%%a
  10.     for /f "delims=:" %%i in ('findstr /o .* temp%%a') do (
  11.         set /a x%%a=%%i-y%%a
  12.         if !n%%a! lss !x%%a! set /a n%%a=x%%a
  13.         set y%%a=%%i
  14.     )
  15.     del temp%%a
  16.     set /a n%%a+=4
  17. for /l %%i in (1,1,!n%%a!) do set BS%%a= !BS%%a!
  18. )
  19. (for /f "tokens=1-2*" %%i in (a.txt) do (
  20.     if "%%j"=="" (set f1=%%i) else (set f1=%%i!BS1!&set f1=!f1:~,%n1%!)
  21.     if "%%k"=="" (set f2=%%j) else (set f2=%%j!BS2!&set f2=!f2:~,%n2%!)
  22.     set f3=%%k
  23.     echo,!f1!!f2!!f3!
  24. ))>b.txt
  25. pause
复制代码

作者: aloha20200628    时间: 2024-7-18 11:45

本帖最后由 aloha20200628 于 2024-7-18 12:25 编辑

回复 11# shenlong

参考12楼的经验,谢谢 重写了9楼代码,用以下1.txt示例文件测试效果明显 楼主可试试...
1.txt》
aa  自x车 1232   米
mm命名  abc 5555  米
bbbbb  密码可靠 66  米
同轴3xx   国产321   456 米

作者: shenlong    时间: 2024-7-18 14:30

回复 14# aloha20200628


    万分感谢,达到预期效果。
作者: newswan    时间: 2024-7-19 00:21

本帖最后由 newswan 于 2024-7-19 00:22 编辑

假如知道最大宽度是10
  1. awk '{ printf "%-10s %-10s %-10s\n", $1, $2, $3 }' a.txt
复制代码

作者: qixiaobin0715    时间: 2024-7-19 12:22

回复 12# 娜美
只要计算出每列的最大字节数,以及各列字节数与最大字节数的差值,然后用空格补齐即可。
可能会稍微复杂些,抽空试试。
作者: ppll2030    时间: 2024-7-19 12:31

回复 17# qixiaobin0715


    请问大佬,有没有办法输出到txt还能保持队形的。
上面的大佬们都是cmd下输出结果。
计算最大字符串并补齐空格,这个我也搞出来了。
就是不能输出到txt还保持不变形。
作者: qixiaobin0715    时间: 2024-7-19 12:45

本帖最后由 qixiaobin0715 于 2024-7-19 12:47 编辑

回复 18# ppll2030
输出的txt文件你用“记事本”打开看看。
不同的文本编辑器,字符不同占用宽度可能会有区别。
作者: buyiyang    时间: 2024-7-19 13:00

回复 19# qixiaobin0715


    显示宽度其实是取决于字体,等宽还是比例,全角和半角等,控制台一般是consoles字体,是等宽的,全角输入中英文字符也是相同宽度。
作者: ppll2030    时间: 2024-7-19 13:25

回复 19# qixiaobin0715


    感谢指导。看来是字体问题了。
换了自带记事本,依然混乱。
作者: ppll2030    时间: 2024-7-19 13:27

回复 20# buyiyang


    感谢解惑。更换字体后txt文件得到对齐的结果了。
作者: newswan    时间: 2024-7-19 13:43

本帖最后由 newswan 于 2024-7-19 14:32 编辑

1 字体的宽度,夹杂中文,更纱一个字体,能完全对齐
2 中文的宽度,上面的所有方案,都没有计算中文宽度,用制表符处理误差,一旦前面中文过多,也不能对齐。
作者: qixiaobin0715    时间: 2024-7-19 14:50

本帖最后由 qixiaobin0715 于 2024-7-19 14:54 编辑

按照各列字节数与最大字节数的差值,然后用空格补齐的方法,适用含有中文字符的列:
  1. @echo off
  2. set colsn=4
  3. set /a colsn-=1
  4. for /f "tokens=1-%colsn%" %%1 in (a.txt) do (
  5.     echo,%%1>>temp1
  6.     echo,%%2>>temp2
  7.     echo,%%3>>temp3
  8. )
  9. setlocal enabledelayedexpansion
  10. for /l %%a in (1,1,%colsn%) do (
  11.     echo,a>>temp%%a
  12.     set /a x=n%%a=0
  13.     for /f "tokens=1,2 delims=:" %%i in ('findstr /n /o .* temp%%a') do (
  14.         set /a "x%%a=%%j-x"
  15.         set /a "m=%%i-1"
  16.         set "n%%a#!m!=!x%%a!"
  17.         if !n%%a! lss !x%%a! set /a "n%%a=x%%a"
  18.         set "x=%%j"
  19.     )
  20.     del temp%%a
  21. )
  22. for /f "tokens=1-%colsn%*" %%1 in (a.txt) do (
  23.     set /a "n+=1"
  24.     set f=
  25.     set "f1=%%1"
  26.     set "f2=%%2"
  27.     set "f3=%%3"
  28.     for /l %%x in (1,1,%colsn%) do (
  29.         set /a "m%%x=n%%x-n%%x#!n!"
  30.         for /l %%y in (1,1,!m%%x!) do set "f%%x=!f%%x! "
  31.         set "f=!f!!f%%x!    "
  32.     )
  33.     echo,!f!%%4
  34. )
  35. pause
复制代码
由于未及仔细考虑,代码存在以下缺点:
1.代码过于复杂,可读性差;
2.除最后一列外,其余各行各列字节数均预先设置变量,处理较大文件时,速度可能会比较慢,甚至超出变量设置的上限而出错;
3.上面代码是按4列文本来处理的,如果列数有变化,需要对以上代码进行修改(虽然写代码时,考虑到了列数变化时,尽量减少修改量),通用性不是太好。

下面举例说明列数变化时,代码调整的方法:
1.如果文本内容的列数为3列,代码做如下调整:
第2行变量值修改为3;
删除第7行;
删除第27行;
将第33行的%%4改为%%3;
2.如果文本内容的列数为5列,代码做如下调整:
第2行变量值修改为5;
第7行下面增加一行:echo,%%4>>temp4
第27行下面增加一行:set "f4=%%4"
将第33行的%%4改为%%5;
作者: ppll2030    时间: 2024-7-19 15:19

我也发一个吧。也是用字节数补齐空格的方式。
就是代码啰嗦了一些。而且局限列数为5列。
不知道还能不能精简,望各位老大指点一二。
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. rem 计算字符的字节,cmd下输出是对齐状态,导出txt会变形,需改换等宽字体,cmd默认是Consolas。
  4. REM 获取文本最大字符串的字节
  5. for /f "delims=" %%a in (1.txt) do for %%s in (%%a) do (
  6. set/p="%%s"<nul>$
  7. for %%b in ($) do if %%~zb gtr !maxZ! set "maxZ=%%~zb"
  8. )
  9. for /f "tokens=1-4*  delims= " %%a in (1.txt) do (
  10.     rem 获取每列字符串的字节
  11.     set "a=%%a"&set "b=%%b"&set "c=%%c"&set "d=%%d"&set "e=%%e"
  12.     set/p="%%a"<nul>$&for %%i in ($) do (set az=%%~zi)
  13.     set/p="%%b"<nul>$&for %%i in ($) do (set bz=%%~zi)
  14.     set/p="%%c"<nul>$&for %%i in ($) do (set cz=%%~zi)
  15.     set/p="%%d"<nul>$&for %%i in ($) do (set dz=%%~zi)
  16.     set/p="%%e"<nul>$&for %%i in ($) do (set ez=%%~zi)
  17.     rem 获取每列字符串的字节差,并用空格补齐
  18.     set/a Na=!maxZ!-!az!&set/a Nb=!maxZ!-!bz!&set/a Nc=!maxZ!-!cz!&set/a Nd=!maxZ!-!dz!&set/a Ne=!maxZ!-!ez!
  19.     for /l %%i in (1,1,!Na!) do set "a=!a! "
  20.     for /l %%i in (1,1,!Nb!) do set "b=!b! "
  21.     for /l %%i in (1,1,!Nc!) do set "c=!c! "
  22.     for /l %%i in (1,1,!Nd!) do set  "d=!d! "
  23.     rem for /l %%i in (1,1,!Ne!) do set  "e=!e! "
  24.     echo !a! !b! !c! !d! !e!
  25. )
  26. del/q $&pause&exit
复制代码

作者: newswan    时间: 2024-7-19 21:59

本帖最后由 newswan 于 2024-7-20 01:54 编辑

保存为 a.awk ,然后 awk -f a.awk a.txt
  1. BEGIN {
  2. FS = " "
  3. while ( getline < ARGV[1] ) {
  4. for (i = 1; i <= NF; i++) {
  5. if ( length($i) > maxlen[i] ) {
  6. maxlen[i] = length($i)
  7. }
  8. }
  9. }
  10. }
  11. {
  12. for (i = 1; i <= NF; i++) {
  13. printf "%-*s\t",maxlen[i],$i
  14. }
  15. printf "\r\n"
  16. }
复制代码

作者: Five66    时间: 2024-7-19 22:13

不如将空格换成tab
文本编辑器来打开时会自行处理
作者: newswan    时间: 2024-7-19 22:26

powershell
  1. $file = Get-Content -Path "data.txt" -Encoding UTF8
  2. $maxlen = @(0) *10
  3. $file | ForEach-Object {
  4. $arr = $_  -split "\s+"
  5. for ( $i =0 ; $i -lt $arr.count ; $i++ ){
  6. if ( $maxlen[$i] -lt ($arr[$i]).length ) {
  7. $maxlen[$i] = ($arr[$i]).length
  8. }
  9. }
  10. }
  11. $maxlen = $maxlen -join " " -replace "( 0)*" -split "\s+"
  12. $file | ForEach-Object {
  13. $arr = $_  -split "\s+"
  14. $str = ""
  15. for ( $i =0 ; $i -lt $arr.count ; $i++ ){
  16. $str += ($arr[$i]).PadRight($maxlen[$i]," ") + "  "
  17. }
  18. write-host ( $str -replace "\s$","" )
  19. }
复制代码

作者: 娜美    时间: 2024-7-20 13:55

本帖最后由 娜美 于 2024-7-20 13:58 编辑

回复 26# newswan

长度如果不超过宽度基本没有问题

   但如遇到这样的长短都不一
  1. aa          1232    米
  2. bbbbb     66       米
  3. b 66  米
  4. cccccccccccccccccccccccccccccccccccc    c   cccccccccccccccccccccccccccccc  cc   c          米 c
  5. c cc    ccc     v          米
  6. 补补补补补补补补补补补补补补补补补  cccccccccccccccccccccccccccccc          补补补补补补补补补补补补补补补补补       c       c                 c
  7. 补  c                      cccccccccccccccccccccccccccccc    c             c                c
复制代码
想要完全解决问题,  似乎需要检查每1列最后一个空格在什么位置, 记录后一个空格位置数值,  再用空格来填充所有,   才可以彻底解决
作者: buyiyang    时间: 2024-7-20 17:57

回复 20# buyiyang

批处理没有格式化输出,要对齐要注意两点:
    1.等宽字体和比例字体。等宽字体每个西文宽度相同,才能对齐;而比例字体会将西文宽度按比例调整以便于观看。中文一般都是等宽的。
    2.全角和半角。半角字符占1个标准字符宽度,西文(包括空格)一般为半角;而全角字符占2个标准字符宽度,中文均为全角。
楼上批处理是根据字节计算宽度的,有findstr算偏移量、for的%%~zi增强扩展算size的,都是根据:
    在ansi(gb2312)编码中一个汉字必须是两个值大于127的字节连在一起,并兼容ASCII。所以一个汉字=2个字节=2个标准字符宽度,一个ASCII字符=1个字节=1个标准字符宽度。
其实可以通过if比较区分每个字符是中文还是英文。
还有通过控制台437代码页将双字节的字符分成两个字符来算字符宽度的方法。
如果是utf-8等其他编码方式则会不同。
作者: buyiyang    时间: 2024-7-20 17:59

本帖最后由 buyiyang 于 2024-7-20 21:55 编辑

回复 25# ppll2030

这个不限列数,还用了两种方法算字符宽度。
  1. @echo off
  2. chcp 437 >nul
  3. set "Separator=@"
  4. set "inputfile=a.txt"
  5. setlocal enabledelayedexpansion
  6. :loop
  7. set /a column+=1
  8. set /a #C%column%max=mark=0
  9. for /f "tokens=%column%" %%i in (%inputfile%) do (
  10.     call :strlen "%%i"
  11.     if !len! geq !#C%column%max! set /a #C%column%max=len
  12.     set mark=1
  13.     )
  14. if %mark% equ 1 goto :loop
  15. set /a column-=1
  16. chcp 936 >nul
  17. set /a lines=0
  18. (for /l %%i in (1,1,%column%) do call :output %%i)>$.temp
  19. set "Separator="&set arg=" delims=%Separator%" "rem"
  20. set "inputfile=$.temp"&(for /l %%i in (1,1,%lines%) do call :output %%i %arg%)>output_%inputfile%
  21. del /q $ $.temp
  22. pause&exit
  23. :strlen
  24. set "str=_%~1"
  25. set "len=0"
  26. for %%n in (64 32 16 8 4 2 1) do (
  27.     if "!str:~%%n,1!" neq "" (
  28.         set/a "len+=%%n"
  29.         set "str=!str:~%%n!"
  30.         )
  31. )
  32. exit /b
  33. :output
  34. if %1 equ 1 goto :output_2
  35. for /f "tokens=1,%1%~2" %%a in (%inputfile%) do (
  36.     if "%%b"=="" (set "field=-") else set "field=%%b"
  37.     %~3 set/p="!field!"<nul>$&for %%i in ($) do (set len=%%~zi)
  38.     %~3 set /a spaceNum=!#C%1max!-len+2,n+=1
  39.     set "outstr=!outstr!%Separator%!field!"
  40.     %~3 for /l %%i in (1,1,!spaceNum!) do set "outstr=!outstr! "
  41.     )
  42. %~3 if %n% geq %lines% set /a lines=n
  43. %~3 set /a n=0
  44. echo,!outstr!
  45. set "outstr="
  46. exit /b
  47. :output_2
  48. for /f "tokens=%1%~2" %%a in (%inputfile%) do (
  49.     if "%%a"=="" (set "field=-") else set "field=%%a"
  50.     %~3 set/p="!field!"<nul>$&for %%i in ($) do (set len=%%~zi)
  51.     %~3 set /a spaceNum=!#C%1max!-len+2,n+=1
  52.     set "outstr=!outstr!%Separator%!field!"
  53.     %~3 for /l %%i in (1,1,!spaceNum!) do set "outstr=!outstr! "
  54.     )
  55. %~3 if %n% geq %lines% set /a lines=n
  56. %~3 set /a n=0
  57. echo,!outstr!
  58. set "outstr="
复制代码

作者: ppll2030    时间: 2024-7-20 22:53

本帖最后由 ppll2030 于 2024-7-20 23:37 编辑

回复 31# buyiyang


    厉害了。大佬这个应该是完美通用版了。
作者: newswan    时间: 2024-7-21 12:54

本帖最后由 newswan 于 2024-7-21 15:54 编辑

回复 29# 娜美

前面发的都没计算全角字符
#36 #37  计算了全角,用 ":" 分隔字段
作者: aloha20200628    时间: 2024-7-21 13:49

本帖最后由 aloha20200628 于 2024-7-21 14:08 编辑


关于中英文混合文本行的对齐输出最终取决于显示字体
已知的 ‘英文用一个字节,中文用2个字节’ 是指存储单位,而每个字符被物理显示到屏幕上的 ‘显示宽度’ 还是要看所选字体
选择中英文字符等宽字体可以简化算法步骤(因批处的字符串截取偏移量视同中英文字符偏移量=1),但显示观感会大打折扣
而选择中英文字符显示宽度成等分关系的字体(例如英文字符占1个显示宽度,中文字符占2个显示宽度,而cmd简中窗口的默认字体即为此型字体)须实时获取每个字段的显示宽度与 ‘全文最长字段’ 显示宽度的差距,此差距即所谓 ‘每个字段的空格填充量’,由此确保每行各列不等长字段能够对齐显示,据此原理调整了9楼代码如下,并已在cmd简中窗口中实测多种样本(最长混合字段可自定义,列数可自定义)均予顺利通过...
  1. @echo off &setlocal enabledelayedexpansion &set "maxZ=0"
  2. for /f "delims=" %%a in (1.txt) do for %%s in (%%a) do (
  3. set "s=%%s"&set "_len="&(call :_strLen)
  4. if !_len! gtr !maxZ! set "maxZ=!_len!"
  5. )
  6. for /f "delims=" %%a in (1.txt) do (
  7. set "ss=" &for %%s in (%%a) do (
  8. set "s=%%s"&set "_len="&(call :_strLen)
  9. set/a "z=maxZ-_len"&set "_s="&for /l %%k in (1,1,!z!) do set "_s=!_s! "
  10. set "ss=!ss! %%s!_s!"
  11. )
  12. echo,!ss:~1!
  13. )
  14. endlocal&pause&exit/b
  15. :_strLen //逐个字符计算》中文字符累加2,英文字符累加1。
  16.    if not defined s exit/b
  17.    if "!s:~0,1!" gtr "Z" (set/a "_len+=2") else (set/a "_len+=1")
  18.    set "s=!s:~1!" &goto :_strLen
复制代码

作者: aloha20200628    时间: 2024-7-21 15:36


用7楼获取全文最大字段长度的方法(中英文字符按存储字节数计算长度)可以进一步简化34楼所用计算方法中须区分中英文字符的步骤...
  1. @echo off &setlocal enabledelayedexpansion &set "maxZ=0"
  2. for /f "delims=" %%a in (1.txt) do for %%s in (%%a) do (
  3. set/p="%%s"<nul>"0.0"
  4. for %%b in (0.0) do if %%~zb gtr !maxZ! set "maxZ=%%~zb"
  5. )
  6. for /f "delims=" %%a in (1.txt) do (
  7. set "ss=" &for %%s in (%%a) do (
  8. set/p="%%s"<nul>"0.0" &for %%b in (0.0) do set/a "z=maxZ-%%~zb"
  9. set "_s="&for /l %%k in (1,1,!z!) do set "_s=!_s! "
  10. set "ss=!ss! %%s!_s!"
  11. )
  12. echo,!ss:~1!
  13. )
  14. del/q "0.0"&endlocal&pause&exit/b
复制代码

作者: newswan    时间: 2024-7-21 15:41

本帖最后由 newswan 于 2024-7-21 20:35 编辑

支持全角字符
awk -f format-table.awk data.txt
  1. function getWidth(str,LenStr,LenASC,LenHZ) {
  2. LenStr = length(str)
  3. LenASC = gsub( /[\x00-\x7F]/ , "" , str )
  4. LenHZ = length(str) / length("一")
  5. return  LenASC + LenHZ * 2
  6. }
  7. function getLengthHZ(str,LenHZ) {
  8. gsub( /[\x00-\x7F]/ , "" , str )
  9. LenHZ = length(str) / length("一")
  10. return  LenHZ
  11. }
  12. BEGIN {
  13. FS = " "
  14. while ( getline < ARGV[1] ) {
  15. for (i = 1; i <= NF; i++) {
  16. len = getWidth($i)
  17. if ( len > maxWidth[i] ) {
  18. maxWidth[i] = len
  19. }
  20. }
  21. }
  22. # print "--maxWidth--"
  23. # for ( i in maxWidth ) {
  24. # printf "%4s" , maxWidth[i]
  25. # }
  26. # printf "\r\n"
  27. # print "----"
  28. }
  29. {
  30. for (i = 1; i <= NF; i++) {
  31. LenHZ = getLengthHZ($i)
  32. printf "%-*s:" , maxWidth[i] + LenHZ , $i
  33. }
  34. printf "\r\n"
  35. }
复制代码
注意 第 35 行 由于 awk 环境不同,可能是 "+ LenHZ" 可能是 "- LenHZ"
作者: newswan    时间: 2024-7-21 15:43

本帖最后由 newswan 于 2024-7-21 19:56 编辑

支持全角字符
format-table.ps1 "in.txt" "out.txt"
  1. param(
  2. [String]$filenameIn ,
  3. [String]$filenameOut
  4. )
  5. $file = Get-Content -Path $filenameIn -Encoding UTF8
  6. $maxWidth = @{}
  7. $file | ForEach-Object {
  8. $arr = $_  -split "\s+"
  9. for ( $i = 0 ; $i -lt $arr.count ; $i++ ){
  10. $Width = ($arr[$i]).length + ( $arr[$i] -replace "[\x00-\x7F]","" ).length
  11. if ( $maxWidth[$i] -lt $Width ) {
  12. $maxWidth[$i] = $Width
  13. }
  14. }
  15. }
  16. $strTable = [System.Collections.ArrayList]@()
  17. $file | ForEach-Object {
  18. $arr = $_  -split "\s+"
  19. $str = ""
  20. for ( $i = 0 ; $i -lt $arr.count ; $i++ ){
  21. $LenHZ = ( $arr[$i] -replace "[\x00-\x7F]","" ).length
  22. $str += ($arr[$i]).PadRight( $maxWidth[$i] - $LenHZ , " " ) + ":"
  23. }
  24. [void]$strTable.add( $str ) #$str -replace "\s$",""
  25. }
  26. $strTable | Out-File -Encoding 'UTF8' -FilePath $filenameOut
复制代码

作者: aloha20200628    时间: 2024-7-21 15:50

本帖最后由 aloha20200628 于 2024-7-21 15:59 编辑

回复 1# shenlong

跑完35层楼,总算能踏踏实实给楼主一个用批处拿下本帖(最长混合字段可自定义,列数可自定义)的解决方案了...

作者: shenlong    时间: 2024-7-21 16:55

回复 38# aloha20200628

多谢,这次真学到了。
作者: buyiyang    时间: 2024-7-21 17:00

回复 36# newswan


    第35行应该是maxWidth - LenHZ
作者: buyiyang    时间: 2024-7-21 17:09

回复 35# aloha20200628


    使用这段代码如果字段中有=,等批处理分隔符的话会被错误分割;而且以“全文最大字段长度”为标准,当原文件字段长度差距悬殊时会使结果中字符较少的列视觉间隔过大而不协调。
作者: 娜美    时间: 2024-7-21 17:30

本帖最后由 娜美 于 2024-7-21 17:40 编辑

我带着好奇心, 跟着这个贴题测过上面你们所说的解决方案,  都没有通过以下文本测试,   有的虽然解决了在cmd屏幕里对齐,  但输出到文本又是另一个不对齐现象发生了
测试要对齐文本 a.txt
  1. aa          1232    米
  2. bbbbb     66       米
  3. b 66  米
  4. cccccccccccccccccccccccccccccccccccc    c   cccccccccccccccccccccccccccccc  cc   c          米 c
  5. c cc    ccc     v          米
  6. 补补补补补补补补补补补补补补补补补  cccccccccccccccccccccccccccccc          补补补补补补补补补补补补补补补补补       c       c                 c
  7. 补  c                      cccccccccccccccccccccccccccccc    c             c                c
复制代码
Out.txt  请复制到txt文本中观看才能看到对齐大概样式
对齐后, 输出到文本中它们大概至少应该是这样的吧,  但是都相差甚远呢,  到目前为止还没有解决输出到文本对齐问题
  1. aa                                                          1232                                                   米
  2. bbbbb                                                    66                                                      米
  3. b                                                            66                                                      米
  4. cccccccccccccccccccccccccccccccccccc    c                                                        cccccccccccccccccccccccccccccc             cc            c                米 c
  5. c                                                            cc                                                       ccc                                                        v             米
  6. 补补补补补补补补补补补补补补补补补       cccccccccccccccccccccccccccccc          补补补补补补补补补补补补补补补补补       c             c                 c
  7. 补                                                          c                                                         cccccccccccccccccccccccccccccc             c             c                 c
复制代码

作者: buyiyang    时间: 2024-7-21 17:34

有的虽然解决了在cmd屏幕里对齐,  但输出到文本但输出到文本又是另一个不对齐现象发生了
娜美 发表于 2024-7-21 17:30

先看看我20楼和30楼说的字体问题……
作者: newswan    时间: 2024-7-21 19:34

回复 40# buyiyang


对,由于 awk 使用环境不同,有的地方是 + 有的地方是 -
作者: newswan    时间: 2024-7-21 20:00

本帖最后由 newswan 于 2024-7-21 20:05 编辑

回复 42# 娜美

#36 #37 输出正确
输出到屏幕正确,保存到文件也应该正确,文件不能对齐,是因为编辑器的字体问题
作者: ppll2030    时间: 2024-7-21 21:01

回复 38# aloha20200628


    还是老大厉害啊。完美解决列数问题,还有全角空格等字符的问题。代码依旧如此简单明了。
作者: ppll2030    时间: 2024-7-21 23:37

回复 38# aloha20200628


    老大,发现一个bug:
如果某组字符中带有*号,这组字符就会被整体抹掉。
而带有英文"!"感叹号的,只会是感叹号抹掉,不会影响这组字符的其他字符。
貌似你的代码第7行中分别提取每行中每组字符的时候,带*号的这组就会被抹掉。
我自己用tokens来提取不会有这个问题,但是我的代码太繁琐,也有列数限制。
所以还是倾向于你的代码,请问可有解决办法?
作者: qixiaobin0715    时间: 2024-7-22 09:50

回复 47# ppll2030
感叹号的问题是变量延迟造成的,你可以试试如果感叹号成对出现,感叹号以及一对感叹号之间的字符都会“隐藏”。*的问题没有遇见过,还是留给大佬们来解释吧。
作者: qixiaobin0715    时间: 2024-7-22 09:53

回复 41# buyiyang
是的,相似的字符还有分号、跳格。
作者: aloha20200628    时间: 2024-7-22 16:23

本帖最后由 aloha20200628 于 2024-7-23 11:19 编辑


若批处字符串里包含转义字符,有如一旦撞上 “台风” 就是 “天灾”,只能尽量减免吧...
更新35楼代码如下,用递归子过程替换 for %%s in (%%a) do ... 以便纠正 ,;= 等其余系统默认分隔符以及*?通配符的影响...
  1. @echo off &setlocal &set "maxZ=0"
  2. for /f "delims=" %%a in (1.txt) do for /f "delims=   " %%b in ("%%a") do (
  3. set "a=%%a"&call :getMaxZ
  4. call set/p=%%maxZ%%<nul>0.0
  5. set/p maxZ=<0.0
  6. )
  7. for /f "delims=" %%a in (1.txt) do for /f "delims=   " %%b in ("%%a") do (set "a=%%a"&set "ss="&call :printAll)
  8. del/q "0.0" 2>nul&endlocal &pause &exit/b
  9. :getMaxZ
  10. for /f "tokens=1* delims=   " %%1 in ("%a%") do (set "a=%%2"&set/p="%%1"<nul>"0.0")
  11. if "%a%"=="" (exit/b) else for %%v in (0.0) do (set "v=%%~zv")
  12. (if %v% gtr %maxZ% set "maxZ=%v%") & goto :getMaxZ
  13. :printAll
  14. for /f "tokens=1* delims=   " %%1 in ("%a%") do (set "s=%%1"&set "a=%%2"&set/p="%%1"<nul>"0.0")
  15. for %%v in (0.0) do (set/a "z=maxZ-%%~zv")
  16. setlocal enabledelayedexpansion &set "_s=" &for /l %%k in (1,1,!z!) do (set "_s=!_s! ")
  17. set "ss=!ss! !s!!_s!" &if "%a%"=="" (set/p="!ss:~1!"<nul&echo,&endlocal&exit/b)
  18. endlocal &set "ss=%ss%" &goto :printAll
复制代码

作者: 77七    时间: 2024-7-22 16:47

回复 47# ppll2030


  
  1. for %%a in (1 2 *xx ?xx 5) do echo %%a
复制代码
含有通配符,*?,又没有通配到文件,自然就消失了。
作者: qixiaobin0715    时间: 2024-7-22 17:00

本帖最后由 qixiaobin0715 于 2024-7-22 17:02 编辑

自己又偷偷的写了一个代码,对for默认分隔符和通配符 *?以及延迟变量中的 ! 免疫,效率不行,没好意思发出来。
作者: ppll2030    时间: 2024-7-22 19:49

回复 51# 77七
回复 48# qixiaobin0715

感谢两位大佬的指点。原因是已经找到了。只是想知道有没有什么好的经验或是取巧的办法可以避免这些通配符。
毕竟还想保持这么精简而干练的代码
作者: ppll2030    时间: 2024-7-22 20:54

回复 50# aloha20200628


    厉害了。换一个思路就搞定了特殊符号问题。这思维真是服了。

不过又有一个新问题: 对于包含有空格或制表符的空行(整行都不显示字符的,看着就像一空行),脚本会卡住,无法进行下去。

不过这不是很大的问题。我目前是加一段去除所有空行的命令,再继续执行就完美了。

再次感谢老大,通过这几段,学习到了很多。
作者: 77七    时间: 2024-7-22 22:54

回复 53# ppll2030


   50楼大佬已经给出答案了。如果没有其它特殊字符,只有通配符,也可以试试以下思路(老帖子看到的),将行变成竖排
  1. @echo off
  2. set "str=1  2 3 *  ?    "
  3. set "str=%str: =&echo;%"
  4. for /f %%a in ('echo %%str%%') do (
  5. echo [%%a]
  6. )
  7. pause
复制代码

作者: qixiaobin0715    时间: 2024-7-23 10:28

本帖最后由 qixiaobin0715 于 2024-7-24 09:40 编辑

回复 53# ppll2030
字符串分行显示时,不使用for %%a in (...) do...这样的循环结构,可以避免出现默认分隔符以及*?通配符的影响;
至于变量延迟中!会”隐藏“的问题有多种解决方法,其中一种方法可以参考http://www.bathome.net/thread-11193-1-1.html
作者: ppll2030    时间: 2024-7-23 10:42

回复 55# 77七


    哈哈。果然有取巧的办法。感谢指导

不过这个用在这里会引发另一个bug

当每行开头是几个空格开始的情况,取值第一个会是ECHO
作者: aloha20200628    时间: 2024-7-23 10:51

本帖最后由 aloha20200628 于 2024-7-23 11:18 编辑

回复 54# ppll2030

更新了50楼代码,滤掉了全空白字符行(仅包含中英文空格+制表符),以免运行中被阻塞...

作者: ppll2030    时间: 2024-7-23 11:25

回复 56# qixiaobin0715


    你的代码有些还看不明,需要研究一下
不过31楼的大佬,貌似也是按列来取值,然后根据每列最大字符串得到每列最大宽度。从而使得结果排版更加的完美。
而它第一段可以自动识别最大的列数。不需要手动填写,或许可以借鉴到你的代码里。
他的代码后半段很复杂。我一时半会不能理解透。
等研究透aloha的版本,再去研究31楼的。毕竟aloha的版本感觉思路跟我的有相似之处,好理解一些。
作者: ppll2030    时间: 2024-7-23 13:03

回复 58# aloha20200628


    老大。你的delims那边的有点问题,害我找了好久。
就是那个中英文空格你给打了三个。直接卡当了。修改为一个制表符和一个空格就OK了
像我这边,还加入全角符的空格,也完美支持了。标点符号也完美显示。
大赞哦~!


作者: aloha20200628    时间: 2024-7-23 14:17

本帖最后由 aloha20200628 于 2024-7-23 14:37 编辑

回复 60# ppll2030

我说的过滤掉中英文空格+制表符构成的 ‘全空白行’,其中的中文空格即全角空格。delims表达式指定的这三个字符复制到代码区中有时会 ‘变形’ 不过知道此意你在自己的代码中调整就是了

作者: 77七    时间: 2024-7-23 15:49

本帖最后由 77七 于 2024-7-23 15:52 编辑

回复 57# ppll2030


   
去掉就行,提供两种方法...
  1. @echo off
  2. set "str=    1  2 3 *  ?    "
  3. set "str=x %str%"
  4. set "str=%str: =&echo;%"
  5. for /f "skip=1" %%a in ('echo %%str%%') do (
  6. echo [%%a]
  7. )
  8. pause
  9. for /f "tokens=*" %%a in ("    1  2 3 *  ?    ") do (
  10. set str=%%a
  11. if defined str (
  12. call set "str=%%str: =&echo;%%"
  13. for /f %%a in ('call echo %%str%%') do (
  14. echo [%%a]
  15. )
  16. )
  17. )
  18. pause
复制代码

作者: qixiaobin0715    时间: 2024-7-23 15:53

本帖最后由 qixiaobin0715 于 2024-7-23 15:54 编辑

回复 59# ppll2030
1.排版美不美观个人看法不一,只要达到各列上下对齐的效果,调整列的宽度就是小kiss,增加或减少几个空格而已;
2.如果列数都懒得搞清楚,就太那个了吧。实际上要解决这个问题也不复杂,就是在原有代码外层加上goto循环,再if几下即可。个人感觉goto语句太过难看,很少使用此循环。
作者: buyiyang    时间: 2024-7-23 22:19

算字节长度其实也可以将文本转换为16进制字串进行计算和处理,而且不用担心任何特殊字符。
  1. @echo off&setlocal enabledelayedexpansion
  2. set "input=a.txt"&set /a m=line=1&set "act=set/p=,&set/a m=1"
  3. for %%i in ("%input%") do fsutil file creATenew "%input%.zero" %%~zi >nul
  4. (for /f "tokens=2" %%a in ('fc /b "%input%" "%input%.zero"^|findstr /irc:"[0-9A-F]*: [0-9A-F][0-9A-F] 00"') do (
  5.     if /i "%%a"=="0A" (echo,&set/a m=1,line+=1,col=0) else if /i "%%a"=="0D" (echo,
  6.         for %%l in (!line!) do for /l %%i in (1,1,!col!) do (
  7.             if !C%%i_%%l! gtr !C%%imax! set/a C%%imax=C%%i_%%l
  8.             set "C%%i_%%l="
  9.             ) ) else if /i "%%a"=="20" (%act%) else if /i "%%a"=="09" (%act%) else (
  10.                     set/p=%%a
  11.                     set /a col+=m,m=0
  12.                     set /a C!col!_!line!+=1
  13.                    )
  14.         ))<nul >"%input%.temp"
  15. (for /f "delims=" %%a in (%input%.temp) do (
  16.     set /a col=0
  17.     for %%i in (%%a) do (
  18.         set /a col+=1&set "space="
  19.         set /a size=C!col!max,len=size*2+2
  20.         for /l %%i in (1,1,!size!) do set "space=!space!20"
  21.         set "outstr=%%i!space!"
  22.         for %%i in (!len!) do echo,!outstr:~0,%%i!
  23.         )   
  24.     set /p =0d0a
  25. )   )<nul >"out_%input%"
  26. del /q "%input%.zero" "%input%.temp"
  27. certutil -decodehex -f "out_%input%" "out_%input%" >nul
  28. pause
复制代码

作者: qixiaobin0715    时间: 2024-7-25 15:32

嵌套变量延迟开关, 来消除文本中“!”的影响,弃用for %%i in (...) do...的方法,通吃文本中存在默认分隔符以及*?通配符等字符的影响,使用大多数人熟悉的cmd命令书写代码。感觉效率还可以:
  1. @echo off
  2. echo,>$1
  3. :a
  4. set Em=
  5. set /a colsn+=1
  6. if %colsn% equ 1 (
  7.     (for /f "tokens=1" %%i in (a.txt) do (echo,%%i&set Em=1))>temp
  8. ) else (
  9.     (for /f "tokens=1,%colsn%" %%i in (a.txt) do (echo,%%j&if not "%%j"=="" set Em=1))>temp
  10. )
  11. if not defined Em goto :b
  12. echo,>>temp
  13. findstr /n /o .* temp>$2
  14. setlocal enabledelayedexpansion
  15.     for /f "tokens=1,2 delims=:" %%i in ($2) do (
  16.         set /a m=%%j-x
  17.         set /a n=%%i-1
  18.         set "a!n!=!m!"
  19.         if !max! lss !m! set "max=!m!"
  20.         set "x=%%j"
  21.     )
  22. set /a n+=1
  23.     (for /f "delims=:" %%i in ('findstr /n .* temp') do (
  24.         set str1=
  25.         set str2=
  26.         set /p str1=
  27.         set /a z=max-a%%i
  28.         for /l %%k in (1,1,!z!) do set str2=!str2!
  29.         if %%i neq !n! echo,!str1!!str2!
  30.     ))<temp>$2
  31.     (for /f "tokens=1* delims=:" %%i in ('findstr /n .* $2') do (
  32.         endlocal
  33.         set /p Line1=
  34.         set Line2=%%j
  35.         setlocal enabledelayedexpansion
  36.         if "!Line1!"=="" (echo,!Line2!) else echo,!Line1!    !Line2!
  37.     ))<$1>b.txt
  38. type b.txt>$1
  39. endlocal
  40. if defined Em goto :a
  41. :b
  42. del temp $1 $2
  43. pause
复制代码





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