внутренние ссылки уценки Джекилла

Эта информация скопирована дословно из моего сообщения DosTips: Безопасное разбор почти любого CSV с помощью parseCSV.bat

Довольно часто, что кто-то хочет для анализа CSV с использованием FOR / F. Это простая задача, если вы знаете, что все столбцы заполнены, и в значениях нет запятых, новых строк или кавычек. Предположим, что есть 4 столбца:

@echo off
for /f "tokens=1-4 delims=," %%A in (test.csv) do (
  echo ----------------------
  echo A=%%~A
  echo B=%%~B
  echo C=%%~C
  echo D=%%~D
  echo(
)

Но все усложняется при возникновении любого из следующих условий:

1) Значения могут быть пустыми с последовательными запятыми. FOR / F обрабатывает последовательные разделители как единое целое, поэтому оно отменяет назначение столбца.

2) Цитированные значения могут содержать запятые. FOR / F неправильно обрабатывает цитированную запятую как разделитель столбцов.

3) Цитированные значения могут содержать символы новой строки. FOR / F будет разбивать строку на новой строке и неправильно обрабатывать одну строку как две.

4) Цитированные значения могут содержать парные кавычки, которые представляют одну цитату. Например, "He said, ""Hello there"". Для преобразования "" в " необходим метод ".

Затем возникают вторичные проблемы, которые могут возникать, если включено замедленное расширение.

5) A FOR variable% % A будет поврежден, если он содержит ! (или иногда ^), если замедленное расширение включено, когда переменная расширяется.

Есть довольно простые решения для некоторых из этих проблем, но решение всех из них чрезвычайно сложно (и медленно) с чистой партией.

Я написал гибридную утилиту JScript / batch, называемую parseCSV.bat, которая упрощает и относительно эффективно правильно разбирает почти любой CSV-файл с помощью FOR / F.

parseCSV.bat

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

::************ Documentation ***********
::parseCSV.bat version 1.0
:::
:::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
:::  quotes within quoted values. Two consecutive quotes within a quoted value
:::  are converted into one quote.
:::
:::  Available options:
:::
:::    /I:string = Input delimiter. Default is a comma.
:::
:::    /O:string = Output delimiter. Default is a comma.
:::
:::    /E = Encode output delimiter in value as \D
:::         Encode newline in value as \N
:::         Encode backslash in value as \S
:::
:::    /D = Escape exclamation point and caret for delayed expansion
:::         ! becomes ^!
:::         ^ becomes ^^
:::
:::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"),
    delayed  = args.Exists("D"),
    inDelim  = args.Exists("I") ? args.Item("I") : ",",
    outDelim = args.Exists("O") ? args.Item("O") : ",",
    quote    = false,
    ln, c, n;
while (!stdin.AtEndOfStream) {
  ln=stdin.ReadLine();
  if (!quote) stdout.Write('"');
  for (n=0; n

Я также написал скрипт, который определяет макрос, чтобы помочь разобрать наиболее проблемные CSV-файлы. См. http://www.dostips.com/forum/viewtopic.php?f=3&t=1827 для получения исходной информации о пакетных макросах с аргументами.

define_csvGetCol.bat

::define_csvGetCol.bat version 1.1
::
:: Defines variable LF and macro csvGetCol to be used with
:: parseCSV.bat to parse nearly any CSV file.
::
:: This script must be called with delayedExpansion disabled.
::
:: The %csvGetCol% macro must be used with delayedExpansion enabled.
::
:: Example usage:
::
::   @echo off
::   setlocal disableDelayedExpansion
::   call define_csvGetCol
::   setlocal enableDelayedExpansion
::   for /f "tokens=1-3 delims=," %%A in ('parseCSV /d /e ^

Использование чрезвычайно просто, если вы знаете, что в любых значениях нет запятых или новых строк, и отсроченное расширение не требуется:

test1.csv

"value1 with ""quotes""",value2: No problem!,value3: 2^3=8,value4: (2^2)!=16
value1,,value3,value4
value1,,,value4
value1,,,
,,,value4

test1.bat - не замедленное расширение, никакие запятые или новые строки в значениях

@echo off
for /f "tokens=1-4 delims=," %%A in ('parseCSV ^

- OUTPUT1 -

-------------
A=value1 with "quotes"
B=value2: No problem!
C=value3: 2^3=8
D=value4: (2^2)!=16

-------------
A=value1
B=
C=value3
D=value4

-------------
A=value1
B=
C=
D=value4

-------------
A=value1
B=
C=
D=

-------------
A=
B=
C=
D=value4

Это также довольно просто, если запятые находятся в значениях, если вы знаете символ, который не существует ни в каком значении. Просто укажите уникальный символ для разделителя вывода.

test2.csv

"value1 with ""quotes""","value2, No problem!","value3, 2^3=8","value4, (2^2)!=16"
value1,,value3,value4
value1,,,value4
value1,,,
,,,value4

test2.bat - не замедленное расширение, новые строки или строки в значениях. Обратите внимание, что вся опция должна указываться, если разделитель является символом яда

@echo off
for /f "tokens=1-4 delims=|" %%A in ('parseCSV "/o:|" ^

- OUTPUT2 -

-------------
A=value1 with "quotes"
B=value2, No problem!
C=value3, 2^3=8
D=value4, (2^2)!=16

-------------
A=value1
B=
C=value3
D=value4

-------------
A=value1
B=
C=
D=value4

-------------
A=value1
B=
C=
D=

-------------
A=
B=
C=
D=value4

Требуется немного больше кода, если значения могут содержат символы новой строки или если вы не знаете символ, который не отображается ни в каком значении. Это решение кодирует новые строки, разделители и косые черты как \N, \D и \S. Для декодирования значений требуется отсроченное расширение в пределах цикла, поэтому ! и ^ должны быть экранированы как ^! и ^^.

test3.csv

"2^3=8","(2^2)!=16","Success!",Value4
value1,value2,value3,value4
,,,value4
"value1","value2","value3","value4"
"He said, ""Hey cutie.""","She said, ""Drop dead!""","value3 line1
value3 line2",c:\Windows

test3.bat - разрешить практически любой допустимый CSV без использования макроса.

@echo off
setlocal enableDelayedExpansion

:: Define LF to contain a linefeed (0x0A) character
set ^"LF=^

^" The empty line above is critical - DO NOT REMOVE

for /f "tokens=1-4 delims=," %%A in ('parseCSV /e /d ^

- OUTPUT3 -

---------------------
A=2^3=8
B=(2^2)!=16
C=Success!
D=Value4

---------------------
A=value1
B=value2
C=value3
D=value4

---------------------
A=
B=
C=
D=value4

---------------------
A=value1
B=value2
C=value3
D=value4

---------------------
A=He said, "Hey cutie."
B=She said, "Drop dead!"
C=value3 line1
value3 line2
D=c:\Windows

test4.bat - разрешить практически любые действительные CSV, но теперь используйте макрос %csvGetCol%.

@echo off

:: Delayed expansion must be disabled during macro definition
setlocal disableDelayedExpansion
call define_csvGetCol

:: Delayed expansion must be enabled when using %csvGetCol%
setlocal enableDelayedExpansion
for /f "tokens=1-4 delims=," %%A in ('parseCSV /e /d ^

Выход идентичен test3.bat

Если файл CSV очень велик, тогда гораздо эффективнее сохраните вывод parseCSV.bat во временном файле, а затем используйте цикл FOR / F для чтения временного файла.

Есть все еще пара ограничений, присущих всем FOR / F:

1) Единственный FOR / F не может разобрать более 32 столбцов.

2) Ограничение длины строки в 8191 символах все еще может быть проблемой.

129
задан Sean Allred 11 January 2015 в 18:19
поделиться