Эта информация скопирована дословно из моего сообщения 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 символах все еще может быть проблемой.