У меня есть файл (1,8 Мб) с одной (очень длинной) строкой текста. Значения в этой строке обычно разделяются 13 пробелами. Что я пытаюсь сделать, так это заменить эти 13 пробелов трубой | разделитель, чтобы я мог обрабатывать этот текстовый файл с помощью SSIS.
До сих пор мне не удавалось программно обработать этот файл с помощью пакетного файла.
Я попытался использовать приведенный ниже код, который я получил из другого сообщения SO.
@echo off
REM create empty file:
break>R1.txt
setlocal enabledelayedexpansion
REM prevent empty lines by adding line numbers (find /v /n "")
REM parse the file, taking the second token (*, %%b) with delimiters
REM ] (to eliminate line numbers) and space (to eliminate leading spaces)
for /f "tokens=1,* delims=] " %%a in ('find /v /n "" ^<PXZP_SND_XZ01_GFT10553.dat') do (
call :sub1 "%%b"
REM write the string without quotes:
REM removing the qoutes from the string would make the special chars poisonous again
>>PXZP_SND_XZ01_GFT10553.dat echo(!s:"=!
)
REM Show the written file:
type PXZP_SND_XZ01_GFT10553.dat
goto :eof
:sub1
set S=%*
REM do 13 times (adapt to your Needs):
for /l %%i in (1,1,13) do (
REM replace "space qoute" with "quote" (= removing the last space
set S=!S: "=|!
)
goto :eof
Может ли кто-нибудь помочь мне здесь? Пример моего текстового файла:
96859471/971 AAAA HAWAII 96860471/971 BBBB HAWAII 96861471/971 CCCC HAWAII 96863471/971 DDDD HAWAII
Как мне обойти эту проблему? Я планирую использовать преобразованный файл в SSIS, который знает, как обрабатывать | разделитель.
Встречается ли какой-либо |
в исходном файле?
Неа. Просто \ и /
Используйте соответствующие инструменты.
Set Inp = wscript.Stdin
Set Outp = wscript.Stdout
Outp.Write Replace(Inp.ReadAll, " ", "|")
Использовать
cscript //nologo "C:\Replace13Spaces.vbs" < "c:\folder\inputfile.txt" > "C:\Folder\Outputfile.txt"
Использование регулярных выражений для замены двух и более пробелов на черту.
Set Inp = wscript.Stdin
Set Outp = wscript.Stdout
Set regEx = New RegExp
regEx.Pattern = "\s{2,}"
regEx.IgnoreCase = True
regEx.Global = True
Outp.Write regEx.Replace(Inp.ReadAll, "|")
Есть два других способа справиться с этим.
Как и первый способ, это replace
несколько раз от самого длинного до самого короткого количества заранее определенных пробелов. IE 13, 10, 8 или 5 пробелов.
Split
жало на 2 пробела. Filter
массив для исключения пустых элементов массива. Затем Join
массив с |
в качестве разделителя.
Я получаю сообщение об ошибке «выход. запись всего объекта не поддерживает это свойство или метод». Не уверен, что здесь не так
Это было отредактировано. Это то, что вы получаете за то, что не смотрите на помощь, хотя она у меня была открыта.
Просто дополнительный вопрос по этому поводу. Там есть и другие пробелы переменной длины (8 пробелов и 10 пробелов). Как мне справиться с этим в этом скрипте?
Можете ли вы описать пространства лучше. В IE всегда разрешен 1 пробел (например, между словами) и никогда больше.
Например, 96859471/971 AAAA HAWAII 96860471/971 BBBB HAWAII имеет пробел между 971 и AAAA (или BBBB). Но пробел между Hawaii и 96860471/971 может составлять 13, 10, 8 или 5 пробелов. Но текст обычно соответствовал шаблону «96859471/971 AAAA HAWAII».
for /F
петля не может обрабатывать строки длиннее примерно 8190 символов. Однако есть способ читать файлы с более длинными строками: используя set /P
в цикле вместе с перенаправление ввода <
; set /P
читает не более 1023 символов, если не встречается разрыв строки или конец файла; выполнение его несколько раз для одного и того же дескриптора открытого (перенаправленного ввода) файла позволяет читать очень длинные строки частями по 1023 символа, поскольку set /P
не сбрасывает указатель файла.
Еще одна проблема состоит в том, чтобы возвращать (эхо) очень длинные строки, что снова невозможно с echo
команда из-за ограничения строки примерно в 8190 символов (которое применяется к командным строкам и содержимому переменных). Также здесь помогает поблочная обработка: во-первых, получить символ конца файла (EOF, ASCII 0x1A); затем возьмите текстовую/строковую часть, добавьте EOF и запишите результат во временный файл, используя echo
(который добавляет разрыв строки) вместе с перенаправление вывода >
; затем скопируйте файл на себя, используя copy
, но прочитайте его в текстовом режиме ASCII, чтобы отбросить EOF и все после (отсюда разрыв строки, ранее добавленный echo
), и запишите его в двоичном режиме, чтобы получить точную копию результирующих данных; наконец, введите содержимое файла, используя type
.
Следующий скрипт использует эти методы (см. все пояснительные примечания rem
в коде):
@echo off
setlocal EnableExtensions DisableDelayedexpansion
rem // Define constants here:
set "_INPUT=.\PXZP_SND_XZ01_GFT10553.dat" & rem // (this is the input file)
set "_OUTPUT=.\R1.txt" & rem // (set to `con` to display the result on the console)
set "_TEMPF=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (specifies a temporary file)
set "_SEARCH= " & rem // (this is the string to be found)
set "_REPLAC=|" & rem // (this is the replacement string)
set "_LTRIM=#" & rem // (set to something to left-trim sub-strings)
(set _LF=^
%= blank line =%
) & rem // (this block stores a new-line character in a variable)
rem // This stores an end-of-file character in a variable:
for /F %%E in ('forfiles /P "%~dp0." /M "%~nx0" /C "cmd /C echo 0x1A"') do set "_EOF=%%E"
rem /* The input file is going to be processed in a sub-routine,
rem which accesses the file content via input redirection `<`: */
< "%_INPUT%" > "%_OUTPUT%" call :PROCESS
endlocal
exit /B
:PROCESS
rem // Reset variables that store a partial string to be processed and a separator:
set "PART = " & set "SEP = "
setlocal EnableDelayedExpansion
:READ
rem /* At this point 1023 characters are read from the input file at most, until
rem a line-break or the end of the file is encountered:*/
set "NEW = " & set /P NEW = ""
rem // The read characters are appended to a string buffer that will be processed:
set "PART=!PART!!NEW!"
rem /* Skip processing when the string buffer is empty, which is the case when the end
rem of the file has already been reached: */
:LOOP
if defined PART (
rem /* Make the search string accessible as a `for` meta-variable reference in
rem to not have to use normal (immediate) `%`-expansion, which could cause
rem trouble with some special characters under some circumstances: */
for /F delims^=^ eol^= %%K in ("!_SEARCH!") do (
rem /* Try to split the string buffer at the first search string and store the
rem portion at the right, using sub-string substitution: */
set "RIGHT=!PART:*%%K=!"
rem /* Check whether the split was successful, hence whether a search string
rem even occurred in the string buffer; if not, jump back and read more
rem characters; otherwise (when the end of the file was reached) clear the
rem right portion and continue processing: */
if "!RIGHT!"= = "!PART!" if not defined NEW (set "RIGHT = ") else goto :READ
rem /* Clear the variable that will receive the portion left to the first
rem occurrence of the search string in the string buffer; then replace each
rem occurrence in the string buffer by a new-line character: */
set "LEFT = " & set ^"PART=!PART:%%K=^%_LF%%_LF%!^"
rem /* Iterate over all lines of the altered string buffer, which is now a
rem multi-line string, then get the first line, which constitutes the
rem portion at the left of the first search string; the (first) line is
rem preceded by an `_` just for it not to appear blank, because `for /F`
rem skips over empty lines; this character is removed later: */
for /F delims^=^ eol^= %%L in (^"_!PART!^") do (
rem // Execute the loop body only for the first iteration:
if not defined LEFT (
rem /* Store the (augmented) left portion with delayed expansion
rem disabled in order not to get trouble with `!` in the string: */
setlocal DisableDelayedExpansion & set "LEFT=%%L"
rem // Enable delayed expansion to be able to safely echo the string:
setlocal EnableDelayedExpansion
rem /* Write to a temporary file the output string, which consists of
rem a replacement string (except for the very first time), the left
rem portion with the preceding `_` removed and an end-of-file
rem character; a line-break is automatically appended by `echo`: */
> "!_TEMPF!" echo(!SEP!!LEFT:~1!%_EOF%
rem /* Copy the temporary file onto itself, but remove the end-of-file
rem character and everything after, then type the file content;
rem this is a safe way of echoing a string without a line-break: */
> nul copy /Y /A "!_TEMPF!" + nul "!_TEMPF!" /B & type "!_TEMPF!"
rem /* Restore the environment present at the beginning of the loop
rem body, then ensure the left portion not to appear empty: */
endlocal & endlocal & set "LEFT=_"
)
)
rem // If specified, left-trim the right portion, so remove leading spaces:
if defined _LTRIM (
for /F "tokens=* eol= delims= " %%T in ("!RIGHT!_") do (
for /F delims^=^ eol^= %%S in (^""!NEW!"^") do (
endlocal & set "NEW=%%~S" & set "RIGHT=%%T"
)
setlocal EnableDelayedExpansion & set "RIGHT=!RIGHT:~,-1!"
)
)
rem // Set the replacement string now to skip it only for the first output:
set "SEP=!_REPLAC!"
rem /* Move the right portion into the string buffer; if there is still some
rem amount of text left, jump back to find more occurrences of the search
rem string; if not, jump back and read more characters, unless the end of
rem the file has already been reached: */
set "PART=!RIGHT!" & if defined PART (
if defined NEW if "!PART:~1024!"= = "" goto :READ
goto :LOOP
) else if defined NEW goto :READ
)
)
endlocal
rem // Clean up the temporary file:
del "%_TEMPF%"
exit /B
Существуют следующие ограничения:
!
, *
, ~
и не должна содержать =
;!
;Спасибо за ответ. Тот, который я принял, делает работу за меня. Но я ценю, что вы нашли время написать ответ, чтобы он кому-то помог.
Пожалуйста! Не нужно извиняться за принятие чужого ответа, решение полностью зависит от вас. (Мне действительно нравилось преодолевать некоторые ограничения cmd
...)
Цикл
for /F
не может обрабатывать строки длиннее примерно 8190 символов...