Удалить возврат каретки и перевод строки из составной записи трейлера в SQL? - PullRequest
2 голосов
/ 06 июня 2019

У меня есть результирующий набор данных из запроса SQL, запущенного в SSMS, который содержит запись из 1 строки, которая экспортируется и сохраняется в Блокноте в формате .txt. Однако в записи трейлера автоматически включается шестнадцатеричный элемент управления, включающий запись перевода строки / каретки после объединенного поля (750 символов), которое является записью трейлера. Как я могу устранить это в конце записи трейлера до экспорта в формат .txt? Файл не должен содержать пустых строк.

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

REPLACE(REPLACE('T'+CAST(RIGHT(REPLACE(STR(COUNT(*)),' ','0'),9) AS VARCHAR)+SPACE(740),CHAR(10),''),CHAR(13),'')

Однако при экспорте в формат .txt он по-прежнему включает шестнадцатеричный символ перевода строки и каретки.

REPLACE(REPLACE('T'+CAST(RIGHT(REPLACE(STR(COUNT(*)),' ','0'),9) AS VARCHAR)+SPACE(740),CHAR(10),''),CHAR(13),'')

Ожидаемый результат - файл не будет содержать байтовые символы 0D и 0A, показанные в включенном шестнадцатеричном представлении экспортированного документа.

HEX VIEW OF EXPORTED .TXT FILE

Ответы [ 3 ]

0 голосов
/ 07 июня 2019

По вашим словам (и следующим комментариям) я не могу сказать, находится ли проблема на уровне SQL или вне его при создании текстового файла.В любом случае, вы можете решить эту проблему, используя NGrams8K (ссылка также включает версию VARCHAR (MAX), которая медленнее, чем версия 8K, но все еще противна).

Я частоавтоматизировать процесс ручного обновления сценариев сборки, импортируя их через OPENROWSET (или что-либо еще), изменяя текст, а затем записывая результаты в новый файл, заменяя старый (используя BCP).Ниже приведен код, который поможет вам понять, как я могу использовать функцию NGrams для решения этой проблемы.

Анализ:

DECLARE @someString VARCHAR(8000) =
'blah blah blah.... ;
blah blah     .... ;
blah blah blah.... ;
 ...;';

SELECT
  ng.position,
  ng.token,
  charValue   = ASCII(ng.Token),
  binaryValue = CAST(ng.token AS VARBINARY(2))
FROM samd.NGrams8K(@someString,1) AS ng;

Возвраты (для краткости сокращены):

position             token     charValue   binaryValue
-------------------- --------- ----------- -----------
1                    b         98          0x62
2                    l         108         0x6C
3                    a         97          0x61
4                    h         104         0x68
5                              32          0x20
6                    b         98          0x62
...
...
68                   .         46          0x2E
69                   .         46          0x2E
70                   .         46          0x2E
71                             32          0x20
72                   ;         59          0x3B
73                             13          0x0D
74                             10          0x0A
75                             32          0x20
76                             32          0x20
....

Строки уведомлений 73 и 74?Это два символа, которые вы хотите удалить: CHAR (13) и CHAR (10) AKA 0D и 0A .Вы хотите удалить их.

Используя NGrams или NGrams8k, вы можете получить позицию последнего LF + CR, найдя последний символ (13).

DECLARE @someString VARCHAR(8000) =
'blah blah blah.... ;
blah blah     .... ;
blah blah blah.... ;
 ...;';

SELECT MAX(ng.position)
FROM   samd.NGrams8K(@someString,1) AS ng
WHERE  ASCII(ng.Token) = 13;

Возвращает: 73

Обратите внимание, что я использую переменную (@someString) для демонстрации, если GUI добавляет финальный LF / CR, товам придется импортировать этот файл и назначить содержимое переменной.

DECLARE @someString VARCHAR(8000) =
'blah blah blah.... ;
blah blah     .... ;
blah blah blah.... ;
 ...;';

-- Use STUFF to remove the last CHAR(13)+CHAR(10)
DECLARE @newString VARCHAR(8000) = 
STUFF(
  @someString,
  (
    SELECT MAX(ng.position)
    FROM   samd.Ngrams8K(@someString,1) AS ng
    WHERE  ASCII(ng.Token) = 13
  ),2,'');

Этот код ^^^^ удаляет окончательный LF / CR.

ОБНОВЛЕНИЕ:

Я только что видел ответ Дэвида;если это так, вы можете использовать мое решение, чтобы извлечь файл, изменить его содержимое и написать новый файл.Ниже приведен пример того, как я это делаю (не идеально, но это работает).

CREATE PROC dbo.FileTransform_clean
  @sourceFile NVARCHAR(500),
  @destFile   NVARCHAR(500),
  @badText    NVARCHAR(1000),
  @cleanup    BIT = 1
AS
BEGIN
  -- 0. Prep
  BEGIN
    SET NOCOUNT ON;
    SET @sourceFile = TRIM(@sourceFile);

    DECLARE @pos  SMALLINT       = CHARINDEX('\',REVERSE(@sourceFile));
    DECLARE @path NVARCHAR(4000) = SUBSTRING(@sourceFile,1,LEN(@sourceFile)-@pos),
            @file NVARCHAR(4000) = SUBSTRING(@sourceFile,LEN(@sourceFile)-@pos+2,4000);
    DECLARE @t TABLE (subdirectory NVARCHAR(4000), depth TINYINT, [file] BIT);

    INSERT @t(subdirectory, depth, [file])
      EXEC [master].dbo.xp_DirTree @path,1,1;

    IF NOT EXISTS (SELECT 1 FROM @t AS t WHERE t.subdirectory = @file)
    BEGIN
      DECLARE @error VARCHAR(100) = 
        'The source file, '+ISNULL(@sourceFile,'NULL')+' was not found.';
      PRINT @error;
      GOTO error
    END

    IF OBJECT_ID('tempdb..##import','U') IS NOT NULL DROP TABLE ##import;
    CREATE TABLE ##import(Document VARCHAR(MAX));
  END

  -- 1. File Import
  BEGIN
    PRINT 'Performing file import...';

    DECLARE @SQL NVARCHAR(4000) = 'INSERT INTO ##import(Document)
    SELECT * FROM OPENROWSET (BULK N'''+@sourceFile+''', SINGLE_BLOB) AS Document;';

    EXEC (@SQL);
  END

  -- 2. Transformation
  BEGIN
    PRINT 'Performing file transform...';

    DECLARE @query NVARCHAR(4000) = 
      N'SELECT STRING_AGG(s.item,CHAR(10)) WITHIN GROUP (ORDER BY s.ItemNumber)
        FROM   SQLToolbox_Misc.samd.delimitedSplitAB((SELECT i.Document FROM ##import AS i),CHAR(10)) AS s
        WHERE NOT EXISTS (SELECT 1 FROM STRING_SPLIT('''+@badText+''','','') AS ss 
                  WHERE CHARINDEX(ss.[value],s.item)>0);'

    SET @SQL = 'bcp '+'"'+@query+'" '+'queryout "'+@destFile+'" -c -T -S '+@@SERVERNAME;
    SET @SQL = REPLACE(@sql,CHAR(13)+CHAR(10),'');

    DECLARE @SQLText VARCHAR(8000) = '  Executing:'+CHAR(10)+'  '+@SQL;
    PRINT @SQLText;

    EXEC [master]..xp_cmdshell @SQL;
    IF @cleanup = 1 DROP TABLE ##import;
  END

  error:
END

Этот код делает что-то совершенно другое, но вы можете принять к сведению, как я: 1. Вытащить файл с помощью OPENROWSET 2. Сделать что-то с содержимым (в моем коде я удаляю любой "Плохо"Текст "определяется @badText 3. Напишите новый файл, используя BCP

Надеюсь, это поможет.

0 голосов
/ 07 июня 2019

Возможно, есть более эффективные автоматизированные методы, но чтобы ответить на поставленный вопрос, вы можете снять флажок «Сохранить CR / LF при копировании или сохранении», найденный в операционном документе

0 голосов
/ 07 июня 2019

Когда вы выбираете «Сохранить результаты как» и выбираете «Текстовый файл», результатом является текстовый файл с разделителями табуляции. Каждое поле разделено вкладками, и каждая запись заканчивается CR / LF. Нет способа изменить это, изменив ваш запрос.

enter image description here

Сгенерированный текстовый файл выглядит следующим образом:

enter image description here Файл начинается с маркера порядка байтов EF BB BF, указывающего, что это формат UTF-8. 78, 79 и 7A - это «x», «y» и «z». Они разделены табуляцией (09), и запись заканчивается CR / LF (OD / OA). Затем то же самое для второй записи, также завершается CR / LF.

С другой стороны, вы можете выбрать Скопировать, запустить Блокнот и Вставить текст. Вы получите CR / LF между записями, но не в конце.

enter image description here

...