Необходимо заменить 13 пробелов в 1 очень длинной строке текстового файла

У меня есть файл (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               

Цикл for /F не может обрабатывать строки длиннее примерно 8190 символов...

aschipfl 03.04.2019 22:01

Как мне обойти эту проблему? Я планирую использовать преобразованный файл в SSIS, который знает, как обрабатывать | разделитель.

rvphx 03.04.2019 22:02

Встречается ли какой-либо | в исходном файле?

aschipfl 03.04.2019 22:15

Неа. Просто \ и /

rvphx 03.04.2019 22:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
100
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используйте соответствующие инструменты.

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, "|")

Есть два других способа справиться с этим.

  1. Как и первый способ, это replace несколько раз от самого длинного до самого короткого количества заранее определенных пробелов. IE 13, 10, 8 или 5 пробелов.

  2. Split жало на 2 пробела. Filter массив для исключения пустых элементов массива. Затем Join массив с | в качестве разделителя.

Я получаю сообщение об ошибке «выход. запись всего объекта не поддерживает это свойство или метод». Не уверен, что здесь не так

rvphx 03.04.2019 22:13

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

Noodles 03.04.2019 22:15

Просто дополнительный вопрос по этому поводу. Там есть и другие пробелы переменной длины (8 пробелов и 10 пробелов). Как мне справиться с этим в этом скрипте?

rvphx 03.04.2019 23:41

Можете ли вы описать пространства лучше. В IE всегда разрешен 1 пробел (например, между словами) и никогда больше.

Noodles 03.04.2019 23:44

Например, 96859471/971 AAAA HAWAII 96860471/971 BBBB HAWAII имеет пробел между 971 и AAAA (или BBBB). Но пробел между Hawaii и 96860471/971 может составлять 13, 10, 8 или 5 пробелов. Но текст обычно соответствовал шаблону «96859471/971 AAAA HAWAII».

rvphx 03.04.2019 23:49

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

Существуют следующие ограничения:

  • части строки между двумя последовательными строками поиска (= 5 × SPACE в приведенном выше подходе) должны быть короче примерно 8 190 символов;
  • строка поиска не должна быть пустой, не должна начинаться с !, *, ~ и не должна содержать =;
  • строка замены не должна содержать !;

Спасибо за ответ. Тот, который я принял, делает работу за меня. Но я ценю, что вы нашли время написать ответ, чтобы он кому-то помог.

rvphx 04.04.2019 17:48

Пожалуйста! Не нужно извиняться за принятие чужого ответа, решение полностью зависит от вас. (Мне действительно нравилось преодолевать некоторые ограничения cmd...)

aschipfl 05.04.2019 00:41

Другие вопросы по теме