【问题标题】:Batch Script: Quicker ECHO output to a csv file in a FOR loop?批处理脚本:在 FOR 循环中更快地将 ECHO 输出到 csv 文件?
【发布时间】:2017-02-17 02:10:09
【问题描述】:

我编写了一个脚本,将文本文件中的分号分隔符替换为逗号,但由于第 9 列包含逗号,因此它还在第 9 列的文本周围加上了引号。当我输出到新的文本文件时,它的处理速度很慢,可能需要 4-5 分钟,它正在读取的文本文件是 50MB。有没有更快或更有效的方法来做到这一点?这是我的 FOR 循环:

FOR /f "usebackq tokens=1-9* delims=;" %%a IN ("%FILENAME%") DO (
SET C10=%%j
ECHO(%%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,"%%i",!C10:;=,! >> "%MYPATH%\Filename %MMDDYYYY%.csv")

或者我应该只学习python.....

谢谢。

【问题讨论】:

  • FOR 命令在开始处理之前读取整个文件。我个人使用 Dave Benham 的 Parse CSV。它是一个混合脚本,应该更快地处理文件。 dostips.com/forum/viewtopic.php?t=5702
  • 如果你想要速度,不要使用批处理。
  • 使用文本编辑器编辑文本有什么问题?例如,Vim 会在几秒钟内完成,主要取决于文件所在存储设备的性能。
  • @AlexP:如果您只需要做一次,文本编辑器就可以了(也是首选)。如果你必须定期这样做,那就太糟糕了。
  • @Squashman - 查看我的回答 - 我采纳了您的想法,并修改了实用程序,以便它现在可以正确进行转换,即使输入包含引号文字。

标签: csv batch-file for-loop


【解决方案1】:

使脚本运行“更快”的一件事是避免每次写入操作打开和关闭输出文件

>> "%MYPATH%\Filename %MMDDYYYY%.csv" (
    FOR /f "usebackq tokens=1-9* delims=;" %%a IN ("%FILENAME%") DO (
        SET C10=%%j
        ECHO(%%a,%%b,%%c,%%d,%%e,%%f,%%g,%%h,"%%i",!C10:;=,!
    )
)

不要重定向每一行,而是重定向完整的for 命令。

【讨论】:

  • 我仍然是一个新手批处理脚本编写者,我将如何在脚本中实际使用它,之前发生了什么>>?
  • @MPineda,代码是您问题中代码的直接替换。在>> 之前,您应该拥有相同的代码,至少启用延迟扩展和初始化使用的变量。
  • 我明白了,刚刚试了一下,完美运行,不到 5 秒,谢谢!
【解决方案2】:

如果您想更改分隔符以在 Excel 中正确打开 csv:则无需这样做。有一个(未记录的)技巧告诉 Excel,使用什么字符作为分隔符:

(echo Sep=;) > new.csv
type old.csv >> new.csv
move /y new.csv old.csv

注意:Excel 读取 Sep=;,并使用该分隔符导入文件的其余部分,但它不是电子表格的一部分,因此如果您再次使用 Excel 保存,Sep=; 将丢失(Excel 使用 @987654324 @ 或 ; 作为分隔符,取决于它的安装语言)。

【讨论】:

  • 哦,我得试着记住这个。可以派上用场。
  • 嗯。我知道 sep 选项,但我认为它不会改变实际数据的分隔符。我通常用它来指定分隔符是一个管道,这样当我打开一个带有 csv 扩展名的文件时,Excel 就知道使用管道作为分隔符。
  • @Squashman - Stephan 提议完全按照您的描述使用它。他假设如果 OP 只是想在 Excel 中打开文件,则不需要物理更改文件分隔符。诚然,这个答案并没有回答实际问题。但人们认为必须重新格式化数据的最常见原因之一是可以在 Excel 中打开它,这是重新格式化数据的一个很好的替代方案。 OP 没有说明重新格式化的目的,所以这个答案可能与 OP 相关,也可能不相关。
  • @dbenham:绝对方便——我们经常使用它(不同的本地化)。但请记住:据我所知,这不是csv 定义的一部分。我不知道其他电子表格程序是否会处理它,甚至微软也可能决定在未来版本的 Excel 中不支持它。
  • 什么是“sep”,我如何在我的脚本中实际实现它? “old.csv”是我的文件吗?
【解决方案3】:

Squashman 有一个好主意,使用我的parseCSV.bat utility 进行转换。该实用程序是一个使用批处理和 JScript 的混合脚本。此实用程序比任何纯批处理解决方案都快得多,并且它可以在 XP 以后的任何 Windows 机器上本地运行 - 不需要 3rd 方 exe。

该实用程序实际上是为了允许在批处理文件中通过 FOR /F 方便地解析 CSV 文件。版本 1.0 有一个问题,这使得它不太适合使用该实用程序来转换 CSV 格式以供其他用途 - 转义的引号文字未转义,因此 "" 变为 "。这种转换对于 FOR /F 解析很有用,但它不是有效的 CSV 格式。

我修改了该实用程序,使其具有保留引号文字转义的选项。现在您可以安全地使用以下命令将分号快速转换为逗号分隔符。

parseCSV /I:; /Q:E <input.csv >output.csv

因为 parseCSV 本身就是一个批处理脚本,所以如果您在另一个批处理脚本中使用该命令,则必须使用 call parseCSV

我能够在 2.5 分钟内使用 parseCSV.bat 转换一个 53MB 的文件。

这是 parseCSV 的代码。但是,我不承诺保持此代码为最新。我建议您从DosTips post 获取代码。您还可以在此处找到该实用程序其他功能的详细说明。

@if (@X)==(@Y) @end /* harmless hybrid line that begins a JScrpt comment

::************ Documentation ***********
::parseCSV.bat version 1.2
:::
:::parseCSV  [/option]...
:::
:::  Parse stdin as CSV and write it to stdout in a way that can be safely
:::  parsed by FOR /F. All columns will be enclosed by quotes so that empty
:::  columns may be preserved. It also supports delimiters, newlines, and
:::  escaped quotes within quoted values. Two consecutive quotes within a
:::  quoted value are converted into one quote by default.
:::
:::  Available options:
:::
:::    /I:string = Input delimiter. Default is a comma (,)
:::
:::    /O:string = Output delimiter. Default is a comma (,)
:::
:::         The entire option must be quoted if specifying poison character
:::         or whitespace literals as a delimiters for /I or /O.
:::
:::         Examples:  pipe = "/I:|"
:::                   space = "/I: "
:::
:::         Standard JScript escape sequences can also be used.
:::
:::         Examples:       tab = /I:\t  or  /I:\x09
:::                   backslash = /I:\\
:::
:::    /E = Encode output delimiter literal within value as \D
:::         Encode newline within value as \N
:::         Encode backslash within value as \S
:::
:::    /D = escape exclamation point and caret for Delayed expansion
:::         ! becomes ^!
:::         ^ becomes ^^
:::
:::    /L = treat all input quotes as quote Literals
:::
:::    /Q:QuoteOutputFormat
:::
:::       Controls output of Quotes, where QuoteOutputFormat may be any
:::       one of the following:
:::
:::         L = all columns quoted, quote Literals output as "   (Default)
:::         E = all columns quoted, quote literals Escaped as ""
:::         N = No columns quoted, quote literals output as "
:::
:::       The /Q:E and /Q:N options are useful for transforming data for
:::       purposes other than parsing by FOR /F
:::
:::    /U = Write unix style lines with newline (\n) instead of the default
:::         Windows style of carriage return and linefeed (\r\n).
:::
:::parseCSV  /?
:::
:::  Display this help
:::
:::parseCSV  /V
:::
:::  Display the version of parseCSV.bat
:::
:::parseCSV.bat was written by Dave Benham. Updates are available at the original
:::posting site: http://www.dostips.com/forum/viewtopic.php?f=3&t=5702
:::

::************ Batch portion ***********
@echo off
if "%~1" equ "/?" (
  setlocal disableDelayedExpansion
  for /f "delims=: tokens=*" %%A in ('findstr "^:::" "%~f0"') do echo(%%A
  exit /b 0
)
if /i "%~1" equ "/V" (
  for /f "delims=:" %%A in ('findstr /bc:"::%~nx0 version " "%~f0"') do echo %%A
  exit /b 0
)
cscript //E:JScript //nologo "%~f0" %*
exit /b 0


************ JScript portion ***********/
var args     = WScript.Arguments.Named,
    stdin    = WScript.Stdin,
    stdout   = WScript.Stdout,
    escape   = args.Exists("E"),
    literalQ = args.Exists("L"),
    escapeQ  = (args.Item("Q")&&args.Item("Q").toUpperCase()=="E"),
    quoteCol = (args.Item("Q")&&args.Item("Q").toUpperCase()=="N") ? '' : '"',
    delayed  = args.Exists("D"),
    inDelim  = args.Item("I") ? eval('"'+args.Item("I")+'"') : ",",
    outDelim = args.Item("O") ? eval('"'+args.Item("O")+'"') : ",",
    newline  = args.Exists("U") ? "\n" : "\r\n",
    quote    = false,
    ln, c, n, out;
while (!stdin.AtEndOfStream) {
  ln=stdin.ReadLine();
  out="";
  if (!quote) stdout.Write(quoteCol);
  for (n=0; n<ln.length; n++ ) {
    c=ln.charAt(n);
    if (c == '"') {
      if (literalQ) {
        if (escapeQ) c+='"';
      } else if (quote && ln.charAt(n+1) == '"') {
        n++;
        if (escapeQ) c+='"';
      } else {
        quote=!quote;
        continue;
      }
    }
    else if (c == inDelim && !quote) c=quoteCol+outDelim+quoteCol;
    else if (escape) {
      if (c == outDelim) c="\\D";
      if (c == "\\") c="\\S";
    }
    else if (delayed) {
      if (c == "!") c="^!";
      if (c == "^") c="^^";
    }
    out+=c;
  }
  out += (quote) ? ((escape) ? "\\N" : newline) : quoteCol+newline;
  stdout.Write(out);
}

【讨论】:

    猜你喜欢
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2011-06-07
    • 2023-04-08
    • 2013-06-11
    相关资源
    最近更新 更多