Помогите в написании пакетного скрипта для разбора файла CSV и вывода текстового файла - PullRequest
8 голосов
/ 24 июня 2011

Я изо всех сил пытаюсь написать пакетный скрипт, который может читать файл CSV, такой как ниже

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

В файле CSV будет строка заголовка. Это должно вывести в текстовый файл как ниже:

Name:Mark
City:London
Country:UK

Name:Ben
City:Paris
Country:France

Name:Tom
City:Athens
Country:Greece

Разделитель полей (:) в вышеприведенном выводе, как ожидается, будет предоставлен в самой строке заголовка. Поэтому все, что мне нужно сделать, это объединить заголовок поля и его значение.

Количество столбцов в этом CSV-файле не является фиксированным, поэтому сценарий не должен ограничиваться 3 токенами. Пожалуйста, помогите!

Ответы [ 4 ]

7 голосов
/ 15 декабря 2011

Я знаю, что это старый вопрос, но этот тип вопросов является моим любимым, поэтому вот мой ответ:

@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

Эта программа не имеет ограничений по количеству полей.Эта версия требует заключать в кавычки элементы, которые могут иметь пробелы или другие разделители пакетов, но это ограничение может быть легко исправлено.

7 голосов
/ 25 июня 2011
@ECHO OFF
IF "%~1"=="" GOTO :EOF
SET "filename=%~1"
SET fcount=0
SET linenum=0
FOR /F "usebackq tokens=1-<b><i>10</i></b> delims=," %%a IN ("%filename%") DO ^
CALL :process <b><i>"%%a" "%%b" "%%c" "%%d" "%%e" "%%f" "%%g" "%%h" "%%i" "%%j"</i></b>
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

Этот пакетный сценарий:

  • принимает один параметр - имя файла для обработки;

  • не проверяетналичие : в конце токена заголовка, и когда значения отображаются, они помещаются сразу после соответствующих токенов заголовка;

  • обрезает все начальные пробелы (но неконечные);

  • считает первую строку строкой заголовка, которая также определяет количество токенов, которые нужно обработать в последующих строках;

  • поддерживает до 10 токенов, и за это отвечают две области, выделенные жирным курсивом (поэтому, когда вам нужно изменить максимальное число, измените обе области: если вы увеличиваете число, вы должны расширить список "%%a" "%%b" "%%c" …, иАналогично, если вы уменьшите число, а затем сократите список).

2 голосов
/ 24 июня 2011

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'

Редактировать: Я думаю, вам нужно что-то родное для командной оболочки Windows.Ну хорошо.

0 голосов
/ 20 октября 2011
  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
...