windows 帮助编写批处理脚本来解析 CSV 文件并输出文本文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6470570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 08:30:54  来源:igfitidea点击:

Help in writing a batch script to parse CSV file and output a text file

windowstextcsvbatch-filerecord

提问by Benny

I am struggling to write a batch script which can read a CSV file such as below

我正在努力编写一个可以读取 CSV 文件的批处理脚本,如下所示

Name:, City:, Country:
Mark, London, UK
Ben, Paris, France
Tom, Athens, Greece

There will be a heading row in the CSV file. It should output to a text file as below:

CSV 文件中会有一个标题行。它应该输出到一个文本文件,如下所示:

Name:Mark
City:London
Country:UK

Name:Ben
City:Paris
Country:France

Name:Tom
City:Athens
Country:Greece

The field separator (:) in the above output is expected to be provided in the header row itself. So all that I need to do is concatenate the field heading and its value.

上述输出中的字段分隔符 (:) 应在标题行本身中提供。所以我需要做的就是连接字段标题和它的值。

The number of columns in this CSV file is not fixed, so the script should not limit to 3 tokens. Kindly help!

此 CSV 文件中的列数不固定,因此脚本不应限制为 3 个标记。请帮助!

采纳答案by Andriy M

@ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-10 delims=," %%a IN ("%filename%") DO ^
CALL :process "%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"
GOTO :EOF

:trim
SET "tmp=%~1"
:trimlead
IF NOT "%tmp:~0,1%"==" " GOTO :EOF
SET "tmp=%tmp:~1%"
GOTO trimlead

:process
SET /A linenum+=1
IF "%linenum%"=="1" GOTO picknames

SET ind=0
:display
IF "%fcount%"=="%ind%" (ECHO.&GOTO :EOF)
SET /A ind+=1
CALL :trim %1
SETLOCAL ENABLEDELAYEDEXPANSION
ECHO !f%ind%!!tmp!
ENDLOCAL
SHIFT
GOTO display

:picknames
IF %1=="" GOTO :EOF
CALL :trim %1
SET /a fcount+=1
SET "f%fcount%=%tmp%"
SHIFT
GOTO picknames

This batch scipt:

这批脚本:

  • accepts one parameter, the name of the file to process;

  • does not verify the presence of :at the end of a header token, and when the values are displayed they are placed immediately after the corresponding header tokens;

  • trims all the leading spaces (but not the trailing ones);

  • considers the first row to be the header row, which also defines the number of tokens to process in subsequent rows;

  • supports up to 10 tokens, and the two areas highlighted in bold italics are responsible for that (so when you need to change the maximum number, modify both areas: if you increase the number, you must expand the "%%a"?"%%b"?"%%c"?…list, and, likewise, if you decrease the number, then shrink the list).

  • 接受一个参数,要处理的文件名;

  • 不验证是否存在:于标题标记的末尾,并且当显示值时,它们会立即放置在相应的标题标记之后;

  • 修剪所有前导空格(但不是尾随空格);

  • 将第一行视为标题行,这也定义了要在后续行中处理的标记数;

  • 最多支持 10 个令牌,粗斜体突出显示的两个区域对此负责(因此,当您需要更改最大数量时,同时修改这两个区域:如果增加数量,则必须扩展"%%a"?"%%b"?"%%c"?…列表,同样,如果您减少数字,然后缩小列表)。

回答by Aacini

I know this is an old question, but this type of question is my favorite one so here it is my answer:

我知道这是一个老问题,但这类问题是我最喜欢的问题,所以这是我的答案:

@echo off
setlocal EnableDelayedExpansion

rem Create heading array:
set /P headingRow=< %1
set i=0
for %%h in (%headingRow%) do (
    set /A i+=1
    set heading[!i!]=%%~h
)

rem Process the file:
call :ProcessFile < %1
exit /B

:ProcessFile
set /P line=
:nextLine
    set line=:EOF
    set /P line=
    if "!line!" == ":EOF" goto :EOF
    set i=0
    for %%e in (%line%) do (
        set /A i+=1
        for %%i in (!i!) do echo !heading[%%i]!%%~e
    )
goto nextLine
exit /B

This program have not any limit in the number of fields. This version requires to enclose in quotes the elements that may have spaces or other Batch delimiters, but this restriction may be easily fixed.

本程序对字段数没有任何限制。此版本需要将可能有空格或其他批处理分隔符的元素用引号括起来,但此限制很容易解决。

回答by Chris

Python makes this so easy it should be regulated by the government.

Python 使这变得如此简单,它应该由政府监管。

from csv import DictReader

with open('file', 'rb') as file:
    reader = DictReader(file)

    for line in reader:
        for field in reader.fieldnames:
            print '{0}{1}'.format(field.strip(), line[field].strip())

         print '\n'

Edit: I guess you need something native to the Windows command shell. Oh well.

编辑:我猜您需要 Windows 命令外壳的本机内容。那好吧。

回答by Benny

  Function CSVArray(CSVFile)

  Dim comma, quote
  comma = ","
  quote = Chr(34)

  Dim charPos, charVal

  Dim cellPos, colMax, colNum
  colMax  = -1

  Dim cellArray(), cellComplete, cellQuoted, csvRecord

  Dim inCsvSys, inCsv, inRow(), rowCount
  rowCount     = -1
  Set inCsvSys = CreateObject("Scripting.FileSystemObject")
  Set inCsv    = inCsvSys.OpenTextFile(CSVFile,"1",True)
  Do While Not inCsv.AtEndOfStream
    rowCount = rowCount + 1
    Redim Preserve inRow(rowCount)
    inRow(rowCount) = inCsv.ReadLine
  Loop
  inCsv.Close

  For r = 0 to rowCount

    csvRecord = inRow(r)
    colNum = -1
    charPos = 0
    cellComplete = True

    Do While charPos < Len(csvRecord)

      If (cellComplete = True) Then
        colNum       = colNum + 1
        cellPos      = 0
        cellQuoted   = False
        cellComplete = False
        If colNum > colMax Then
          colMax = colNum
          Redim Preserve cellArray(rowCount,colMax)
        End If              
      End If

      charPos = charPos + 1
      cellPos = cellPos + 1
      charVal = Mid(csvRecord, charPos, 1)
      If (charVal = quote) Then
        If (cellPos = 1) Then
          cellQuoted = True
          charVal    = ""
        Else
          Select Case Mid(csvRecord, charPos+1, 1)
          Case quote
            charPos = charPos + 1
          Case comma
            charPos = charPos + 1
            cellComplete = True
          End Select
        End If
      ElseIf (charVal = comma) And (cellQuoted = False) Then
        cellComplete = True
      End If
      If (cellComplete = False) Then
        cellArray(r,colNum) = cellArray(r,colNum)&charVal
      End If

    Loop

  Next
  CSVArray = cellArray
End Function

Dim StdOut
Set StdOut = WScript.StdOut
Dim csv

If Wscript.Arguments.Count = 0 Then
    Wscript.StdOut.WriteLine "Invalid Arguments"
Else
    csv = CSVArray(Wscript.Arguments(0))
End If


For r = 1 to UBound(csv,1)
  For c = 0 to UBound(csv,2)
    Wscript.StdOut.WriteLine csv(0,c) & csv(r,c)
  Next
Next