在文本文件中查找数字并使用命令行更改其行数据的符号
Find a number in text file and change signs for its row data using command line
我正在学习批处理脚本,我遇到的第一个任务是创建一个超过 1000 行的文本文件,类似于:
Organization, month,acct no.,data1,data2,data3,data4
orgA,Jan,1234,78900,78900,78900,78900
我需要帮助来编写一个批处理文件,该文件应该找到特定的 acct no.
(例如:3456
)并在 data1, data2,data3,data4
[ 之前放置一个“-
” =17=]
我试过:
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
这里有一个可能的方法来做你想做的事情——只针对 整数 值(请参阅代码中的解释性注释 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
这是另一种方式——对于 decimal 值,这些值实际上被视为字符串(参见备注 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
这种管理大文件的问题是批处理文件处理本来就很慢,所以任何可以加快处理速度的方法都是好的。
编辑:更改最后四个数据的符号。
第二次编辑: ...当这样的数据可能有小数点时
@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 /?
(
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
命令都用括号括起来,以确保将 echo
es 的输出重定向到已处理的文件名,该文件名不得与源数据文件名相同。
当然,如果需要,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
Organization, month,acct no.,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
这是输出文件
Organization, month,acct no.,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
这似乎是您需要的。
注意:powershell 标签是很久以后才添加到问题中的,所以这个答案应该被认为是非竞争性的。
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-Object
cmdlet 处理每个这样的对象:
- 自动变量
$_
表示每次迭代中手头的输入对象。
if ($_.'acct no.' -eq $acctNo)
检查感兴趣的帐号。
Get-Member -InputObject $_ data*
使用反射 return 名称以 data
. 开头的输入对象的所有属性
foreach(...)
循环处理所有匹配的属性。
$_.$($prop.name) = '-' + $_.$($prop.name)
通过在现有值前面加上 -
来更新每个匹配的 属性。
请注意,您不能将结果直接保存回 相同的 文件 - 除非您使用 (Import-Csv in.csv)
而不是 Import-Csv in.csv
,但是意味着整个输入文件将被读入内存作为一个整体.
我正在学习批处理脚本,我遇到的第一个任务是创建一个超过 1000 行的文本文件,类似于:
Organization, month,acct no.,data1,data2,data3,data4
orgA,Jan,1234,78900,78900,78900,78900
我需要帮助来编写一个批处理文件,该文件应该找到特定的 acct no.
(例如:3456
)并在 data1, data2,data3,data4
[ 之前放置一个“-
” =17=]
我试过: 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
这里有一个可能的方法来做你想做的事情——只针对 整数 值(请参阅代码中的解释性注释 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
这是另一种方式——对于 decimal 值,这些值实际上被视为字符串(参见备注 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
这种管理大文件的问题是批处理文件处理本来就很慢,所以任何可以加快处理速度的方法都是好的。
编辑:更改最后四个数据的符号。
第二次编辑: ...当这样的数据可能有小数点时
@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 /?
(
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
命令都用括号括起来,以确保将 echo
es 的输出重定向到已处理的文件名,该文件名不得与源数据文件名相同。
当然,如果需要,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
Organization, month,acct no.,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
这是输出文件
Organization, month,acct no.,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
这似乎是您需要的。
注意:powershell 标签是很久以后才添加到问题中的,所以这个答案应该被认为是非竞争性的。
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-Object
cmdlet 处理每个这样的对象:- 自动变量
$_
表示每次迭代中手头的输入对象。 if ($_.'acct no.' -eq $acctNo)
检查感兴趣的帐号。Get-Member -InputObject $_ data*
使用反射 return 名称以data
. 开头的输入对象的所有属性
foreach(...)
循环处理所有匹配的属性。$_.$($prop.name) = '-' + $_.$($prop.name)
通过在现有值前面加上-
来更新每个匹配的 属性。
- 自动变量
请注意,您不能将结果直接保存回 相同的 文件 - 除非您使用 (Import-Csv in.csv)
而不是 Import-Csv in.csv
,但是意味着整个输入文件将被读入内存作为一个整体.