2

我正在学习批处理脚本,我遇到的第一个任务是一个文本文件,它有超过 1000 行,是这样的:

Organization, month,acct no.,data1,data2,data3,data4
orgA,Jan,1234,78900,78900,78900,78900

我需要帮助编写一个批处理文件,该文件应该找到一个特定的acct no.(例如:)3456并在前面放一个' -'data1, data2,data3,data4

我试过:1)使用批处理命令:

for /F "tokens=1 delims=," %%a in (%source%) do SET "org=%%a"   
for /F "tokens=2 delims=," %%b in (%source%) do SET "month=%%b"  
for /F "tokens=3 delims=," %%c in (%source%) do SET "acct=%%c"
for /F "tokens=4 delims=," %%d in (%source%) do SET "data1=%%d"
for /F "tokens=5 delims=," %%e in (%source%) do SET "data2=%%e"
for /F "tokens=6 delims=," %%f in (%source%) do SET "data3=%%f"
for /F "tokens=7 delims=," %%g in (%source%) do SET "data4=%%g"

set search=3456
set replace=-%data1%


FOR /F "tokens=* delims=," %%i in ("%source%") do
(set newline=%%i
IF /i %acct% EQU %search%
set newline=!newline:%org%,%month%,%acct%,%replace%! 
echo !newline!>>%target%
)  

2)VBS:

@echo objFile.WriteLine strNewText
@echo objFile.CloseConst ForReading = 
@echo Const FileIn = "test.txt"
@echo Const FileOut = "test_adhoc.txt"  
@echo Set objFSO = CreateObject("Scripting.FileSystemObject")
@echo Set objFile = objFSO.OpenTextFile(FileIn, ForReading)
@echo strText = objFile.ReadAll
@echo objFile.Close
@echo strNewText = Replace(strText, "*,*,3456,*,*,*,*", "*,*,3456,-*,-*,-*,-  *")
@echo Set objFile = objFSO.OpenTextFile(FileOut, ForWriting)
@echo objFile.WriteLine strNewText
@echo objFile.Close
4

4 回答 4

2

这种对大文件进行管理的问题是批处理文件本身就很慢,因此任何可以加快处理速度的方法都是好的。

编辑更改最后四个数据的符号。

第二次编辑:......当这样的数据可能有小数点时

@echo off
setlocal EnableDelayedExpansion

set search=3456

rem Find the number of lines before the target one
for /F "delims=:" %%a in ('findstr /N "^.*,.*,%search%" source.txt') do set /A lines=%%a-1

rem Reading from the source file
< source.txt (

   rem Copy the lines previous to target one
   for /L %%i in (1,1,%lines%) do set /P "line=" & echo !line!

   rem Read and process the target line
   set /P "line="
   for /F "tokens=1-7 delims=," %%a in ("!line!") do (
      set "data1=-%%d" & set "data2=-%%e" & set "data3=-%%f" & set "data4=-%%g"
      echo %%a,%%b,%%c,!data1:--=!,!data2:--=!,!data3:--=!,!data4:--=!
   )

   rem Copy the rest of lines
   findstr "^"

) > output.txt

move /Y output.txt source.txt

在此代码中,目标行是通过一个findstr正则表达式在一个操作中找到的,该正则表达式在该行的第三个逗号分隔字段中搜索期望acct no.。程序的其余部分很简单,不言自明......

如果您对任何命令有任何疑问,您可以使用 /? 查看它对执行它的帮助?范围; 例如:findstr /?

于 2017-04-11T21:17:37.480 回答
1

注意:标签只是在很久以后才添加到问题中,所以这个答案应该被认为是出于竞争。

PowerShell提供了一个简洁而强大的解决方案:

$acctNo = 3456

Import-Csv in.csv | ForEach-Object { 
  if ($_.'acct no.' -eq $acctNo) { 
    foreach($prop in (Get-Member -InputObject $_ data*)) {
      $_.$($prop.name) = '-' + $_.$($prop.name)
    }
  }
  $_ 
} # add, e.g., | Out-File -Encoding utf8 out.csv to save to a (different) file.
  • Import-Csv file读取输入 CSV 文件并将每一行转换为自定义对象,其属性对应于每一行的列值。

  • ForEach-Objectcmdlet 处理每个这样的对象:

    • 自动变量$_表示每次迭代中手头的输入对象。
    • if ($_.'acct no.' -eq $acctNo)检查感兴趣的帐号。
    • Get-Member -InputObject $_ data*使用反射返回名称以 . 开头的输入对象的所有属性data
    • foreach(...)在循环中处理所有匹配的属性。
    • $_.$($prop.name) = '-' + $_.$($prop.name)-通过添加到现有值来更新每个匹配的属性。

请注意,您不能将结果直接保存回同一个文件 - 除非您使用(Import-Csv in.csv)而不是 just Import-Csv in.csv,但这意味着整个输入文件将作为一个整体读入内存。

于 2017-04-15T02:59:28.130 回答
1

这是一种可能的方式来做你想做的事 - 仅适用于数值(请参阅代码中的解释性注释rem):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=.\data.csv"         & rem // (path to CSV file to modify)
set "_TMPF=%TEMP%\%_FILE%.tmp" & rem // (path to temporary file)
set "_ACCT=%~1" & rem // (account number to search, taken from first argument)

rem // Write modified CSV data to temporary file:
> "%_TMPF%" (
    rem // Reset flag to indicate header (first row):
    set "SKIP="
    rem // Read CSV file line by line and extract seven tokens (columns):
    for /F "tokens= 1-7 delims=, eol=," %%A in ('type "%_FILE%"') do (
        rem // Check whether line is header, skip it from modification in case:
        if defined SKIP (
            rem // Check whether current account number matches:
            if /I "%%C"=="%_ACCT%" (
                rem // Assemble first three call values (do not modify):
                set "PREF=%%A,%%B,%%C"
                rem /* Invert sign of remaining four (numeric) cell values;
                rem    instead, you could also simply write this:
                rem    `echo(%%A,%%B,%%C,-%%D,-%%E,-%%F,-%%G`, but this
                rem    would lead to `--` if a number is already negative: */
                set /A "VAL1=-%%D, VAL2=-%%E, VAL3=-%%F, VAL4=-%%G"
                rem // Return modified line:
                setlocal EnableDelayedExpansion
                echo(!PREF!,!VAL1!,!VAL2!,!VAL3!,!VAL4!
                endlocal
            ) else (
                rem // Account number does not match, so return original line:
                echo(%%A,%%B,%%C,%%D,%%E,%%F,%%G
            )
        ) else (
            rem // Line is the header, so return original line:
            echo(%%A,%%B,%%C,%%D,%%E,%%F,%%G
            rem // Next line is certainly not a header:
            set "SKIP=#"
        )
    )
)
rem // Replace original CSV file with temporary file:
> nul move /Y "%_TMPF%" "%_FILE%"

endlocal
exit /B

这是另一种方式——对于十进制值,它实际上被视为字符串(见备注rem):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=.\data-dec.csv"     & rem // (path to CSV file to modify)
set "_TMPF=%TEMP%\%_FILE%.tmp" & rem // (path to temporary file)
set "_ACCT=%~1" & rem // (account number to search, taken from first argument)

rem // Write modified CSV data to temporary file:
> "%_TMPF%" (
    rem // Reset flag to indicate header (first row):
    set "SKIP="
    rem // Read CSV file line by line and extract seven tokens (columns):
    for /F "tokens= 1-7 delims=, eol=," %%A in ('type "%_FILE%"') do (
        rem // Check whether line is header, skip it from modification in case:
        if defined SKIP (
            rem // Check whether current account number matches:
            if /I "%%C"=="%_ACCT%" (
                rem // Assemble first three call values (do not modify):
                set "PREF=%%A,%%B,%%C"
                rem // Invert sign of remaining four (numeric) cell values:
                set "VAL1=-%%D" & set "VAL2=-%%E" & set "VAL3=-%%F" & set "VAL4=-%%G"
                rem // Return modified line, avoiding doubled minus-signs:
                setlocal EnableDelayedExpansion
                echo(!PREF!,!VAL1:--=!,!VAL2:--=!,!VAL3:--=!,!VAL4:--=!
                endlocal
            ) else (
                rem // Account number does not match, so return original line:
                echo(%%A,%%B,%%C,%%D,%%E,%%F,%%G
            )
        ) else (
            rem // Line is the header, so return original line:
            echo(%%A,%%B,%%C,%%D,%%E,%%F,%%G
            rem // Next line is certainly not a header:
            set "SKIP=#"
        )
    )
)
rem // Replace original CSV file with temporary file:
> nul move /Y "%_TMPF%" "%_FILE%"

endlocal
exit /B
于 2017-04-11T20:17:51.713 回答
1
(    
for /f "tokens=1-7delims=," %%a in (yourfilename.txt) do (
 if "%%c"=="3456" (echo %%a,%%b,%%c,-%%d,-%%e,-%%f,-%%g
 ) else (echo %%a,%%b,%%c,%%d,%%e,%%f,%%g)
)
)>processedfilename.txt

应该管用。注意整个for命令用括号括起来,保证echoes的输出重定向到处理后的文件名,不能和源数据文件名相同。

当然,3456如果需要,可以用变量替换。

这是我使用的测试批次 - 它与我发布的代码完全相同,只是构建了适合我的测试系统的文件名。

@ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q43354291.txt"
SET "outfile=%destdir%\outfile.txt"
(    
for /f "tokens=1-7delims=," %%a in (%filename1%) do (
 if "%%c"=="3456" (echo %%a,%%b,%%c,-%%d,-%%e,-%%f,-%%g
 ) else (echo %%a,%%b,%%c,%%d,%%e,%%f,%%g)
)
)>"%outfile%"

GOTO :EOF

这是我使用的输入文件-它只是您的数据,其中有几行重复并固定以适合account=3456

组织,月份,帐户号,data1,data2,data3,data4
orgA,Jan,1234,78900,78900,78900,78900
orgA,Jan,3456,78900,78900,78900,78900
orgA,Jan,6789,78900,78900 ,78900,78900

这是输出文件

组织,月份,账户号,data1,data2,data3,data4
orgA,Jan,1234,78900,78900,78900,78900
orgA,Jan,3456,-78900,-78900,-78900,-78900
orgA,Jan,6789 ,78900,78900,78900,78900

这似乎是您所需要的。

于 2017-04-12T02:12:17.717 回答