【问题标题】:Remove Carriage Return & Line Feed from Concatenated Trailer Record in SQL?从 SQL 中的连接尾记录中删除回车和换行?
【发布时间】:2019-06-06 15:33:40
【问题描述】:

我有一个在 SSMS 中运行的 SQL 查询的结果数据集,其中包括一个 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 字节字符。

【问题讨论】:

  • 该表达式中没有 CR 或 LF。我想它们是由将值写入文本文件的过程生成的。你是如何创建文本文件的?
  • David,在“结果到网格”模式下运行查询后,我目前正在右键单击 SSMS 中显示的结果数据。然后我选择“将结果另存为”并保存一个 .txt 格式的文件,其中包含问题换行和回车控件...谢谢。
  • 用更简单的方法试试。当您尝试选择“x”并执行相同操作时会发生什么?我认为这是添加 CR/LF 的保存过程。
  • @DavidDubois OP 可能还不清楚 - CR 和 LF ... 是 0D 和 0A。请注意这个查询:SELECT CAST(CHAR(13) AS VARBINARY(2)), CAST(CHAR(10) AS VARBINARY(2))..(我可能会误解你的意思)

标签: sql-server tsql replace notepad


【解决方案1】:

当您选择“将结果另存为”并选择文本文件时,结果是一个制表符分隔的文本文件。每个字段由制表符分隔,每个记录由 CR/LF 终止。无法通过更改查询来改变它。

生成的文本文件如下:

该文件以字节顺序标记 EF BB BF 开头,表明它是 UTF-8 格式。 78、79和7A是'x'、'y'和'z'。这些由 TAB (09) 分隔,记录以 CR/LF (OD/OA) 终止。然后第二条记录也一样,也被 CR/LF 终止。

另一方面,您可以选择复制、运行记事本并粘贴文本。您将在记录之间获得 CR/LF,但最后没有。

【讨论】:

  • 关闭,但仍在寻找更自动化的解决方案,因为我让 adTempus 每晚运行这项工作。让我知道是否有其他想法。谢谢。
  • 所以现在您知道,您无法更改查询中的任何内容来解决您的问题。您可以删除所有那些试图删除它们的 REPLACE。我对adTempus一无所知,但我猜你是否可以自动右键单击,选择菜单项并输入文件名,你可能可以自动运行记事本和复制/粘贴。或者您可以处理更小的问题,即简单地找到一种从文件末尾删除两个字节的方法。我敢打赌,使用 DOS 批处理有一些方法,或者如果没有任何开发人员可以在短时间内为您提供这样的方法。
【解决方案2】:

在创建文本文件时,我无法从您所说的内容(以及以下 cmets)中判断问题是在 SQL 级别还是在 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 0D0A。你想删除那些。

使用 NGrams 或 NGrams8k,您可以通过定位最后一个 CHAR(13) 来获得最后一个 LF+CR 的位置。

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写一个新文件

希望这会有所帮助。

【讨论】:

  • 如果 OP 想从现有文件的末尾删除 CR/LF,他可以在记事本中打开它,将光标放在行尾,然后按几次 DEL 即可删除行终止符,然后保存。
  • 不幸的是,正在寻找更自动化的解决方案。
  • 感谢@DavidDubois 的洞察力,人们会震惊地发现您可以使用记事本编辑文本。你应该为此写一篇博文。
【解决方案3】:

可能有更好的自动化方法,但要回答提出的问题,您可以取消选中操作中的“在复制或保存时保留 CR/LF”框

【讨论】:

  • 这会从所有行中删除 CR/LF 吗?我觉得可能。我只希望从预告片记录行的末尾删除。抱歉,如果我在这方面没有完全清楚。
  • 是的,它会的。我以为你只有一行,但你没有是有道理的。
猜你喜欢
  • 2010-10-26
  • 1970-01-01
  • 2017-05-05
  • 2020-10-30
  • 1970-01-01
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多