SQL string[] Pack/Unpack utility functions

Can anyone skilled in t-sql create companion table-valued functions to these C# array pack & unpack utility methods that escape delimiters and preserve nulls, null arrays, empty arrays through roundtrip encoding?

By packing any variety of string arrays into a string, you can pass such (small) arrays to SQL as parameters, which is useful in some scenarios.

static class ArrayUtil
{
    public static string Pack(string[] original)
    {
        return Pack(original, '|', '0', '~');
    }

    public static string[] Unpack(string original)
    {
        return Unpack(original, '|', '0', '~');
    }

    public static string Pack(string[] original, char delimiter, char zed, char escape)
    {
        if (delimiter == escape ||
            zed == escape ||
            delimiter == zed) throw new ArgumentException("special characters must be distinct");

        // Null array returns a null string
        if (original == null) return null;

        // Empty array returns an empty string
        if (original.Length == 0) return string.Empty;

        // Arrays with a single empty element are represented as just the escape character
        // to differentiate from an empty array
        if (original.Length == 1 && original[0] == string.Empty) return escape.ToString();

        // Otherwise
        StringBuilder sb = new StringBuilder();

        for (int i = 0, ol = original.Length; i < ol; i++)
        {
            string s = original[i];

            if (s == null)
            {
                sb.Append(zed); // zed == null
            }
            else
            {
                for (int j = 0, sl = s.Length; j < sl; j++)
                {
                    char c = s[j];

                    // escape literal delimiters, escapes, and leading zeds
                    if (c == delimiter ||
                        c == escape ||
                        (c == zed && j == 0)) sb.Append(escape);

                    sb.Append(c);
                }
            }

            if (i != ol - 1) sb.Append(delimiter); // no trailing delimiter
        }

        return sb.ToString();
    }

    public static string[] Unpack(string original, char delimiter, char zed, char escape)
    {
        if (delimiter == escape ||
            zed == escape ||
            delimiter == zed) throw new ArgumentException("special characters must be distinct");

        // Null string returns a null array
        if (original == null) return null;

        // Empty string returns an empty array
        if (original == string.Empty) return new string[] { };

        // A single escape character represents an array with a single empty element
        // to differentiate from an empty array
        if (original == escape.ToString()) return new string[] { string.Empty };

        // Otherwise
        StringBuilder sb = new StringBuilder(); // A place to store the current element
        StringReader sr = new StringReader(original); // A stream of the original string
        List<string> unpacked = new List<string>(); // The finished elements
        int next;

        while ((next = sr.Read()) >= 0)
        {
            char c = (char)next;

            if (c == zed && sb.Length == 0)
            {
                unpacked.Add(null);

                if ((next = sr.Peek()) >= 0 && (char)next != delimiter)
                    throw new ArgumentException("An element's leading zed character must be escaped or must alone be the element", "original");

                sb = null;
            }
            else if (c == delimiter)
            {
                if (sb != null) unpacked.Add(sb.ToString());

                sb = new StringBuilder();
            }
            else if (c == escape)
            {
                if ((next = sr.Read()) >= 0)
                {
                    sb.Append((char)next);
                }
                else
                    throw new ArgumentException("Escapee expected", "original");
            }
            else
            {
                sb.Append(c);
            }
        }

        // A final zed character will make sb = null, but otherwise we have an additional element
        if (sb != null) unpacked.Add(sb.ToString());

        return unpacked.ToArray();
    }
}

Example packings with Pack(original, '|', '0', '~') , basic case:

["hello", "world"] -> "hello|world"

Some special cases (as discovered by PEX)

null -> null
[null] -> "0"
[null, null] -> "0|0"
[] -> ""
[""] -> "~"
["", ""] -> "|"
["|", "|"] -> "~||~|"

这里是对T-SQL Unpack的一个刺探:

CREATE FUNCTION [dbo].[UnpackStrings]
(
      @original nvarchar(4000), 
      @delimiter nchar(1),
      @zed nchar(1),
      @escape nchar(1)
)
RETURNS 
@unpacked TABLE 
(
      elementNumber INT IDENTITY(1,1), 
      element nvarchar(4000)
)
AS
BEGIN
      DECLARE @next int;
      DECLARE @c nchar(1);
      DECLARE @pos int;
      DECLARE @sb nvarchar(4000);

      -- Special characters must be distinct.
      IF ( (@delimiter = @escape) OR 
             (@zed = @escape) OR 
             (@delimiter = @zed) )
            RETURN;
      -- Null string return a null array
      IF (@original IS NULL)
            RETURN;
      -- A single escape character represents an array with a single 
      -- empty element to differentiate from an empty array.
      IF (@original = @escape)
      BEGIN
            INSERT @unpacked (element) VALUES ('');
            RETURN;
      END
      -- Otherwise read through the string and unpack.
      SET @pos = 1;
      SET @sb = '';
      SET @next = 0;
      -- Fill the table variable with the rows for your result set
      WHILE(  (@pos <= LEN(@original)) AND @next IS NOT NULL )
      BEGIN
            SET @next = UNICODE(SUBSTRING(@original, @pos, 1));
            IF (@next IS NULL)
            BEGIN
                  IF (LEN(@sb) > 0)
                        INSERT @unpacked (element) VALUES (@sb);
                        SET @sb = '';
                  CONTINUE;
            END
            ELSE
            BEGIN
                  SET @c = NCHAR(@next);
                  IF ( @c = @zed AND (LEN(@sb) = 0 OR LEN(@sb) IS NULL) )
                  BEGIN
                        INSERT @unpacked (element) VALUES (NULL);
                        -- need to peek at next character,
                        SET @next = UNICODE(SUBSTRING(@original, @pos+1, 1));
                        IF (@next IS NOT NULL)
                        BEGIN
                              SET @c = NCHAR(@next);
                              IF ( @c != @delimiter )
                              BEGIN
                                    -- Peek at next character and it not delimiter,
                                    -- bad format encountered.
                                    BREAK;
                              END
                        END                     
                        SET @sb = NULL;
                  END
                  ELSE
                  BEGIN
                        IF ( @c = @delimiter )
                        BEGIN
                              IF (LEN(@sb) > 0 )
                                    INSERT @unpacked (element) VALUES (@sb);
                              SET @sb = '';
                        END
                        ELSE
                        BEGIN
                              IF ( @c = @escape )
                              BEGIN
                                    SET @pos = @pos + 1;
                                    SET @next = UNICODE(SUBSTRING(@original, @pos, 1));
                                    IF (@next IS NULL )
                                    BEGIN
                                          CONTINUE;
                                    END
                                    ELSE 
                                    BEGIN 
                                          SET @sb =  @sb +  NCHAR(@next);
                                    END
                              END
                              ELSE
                              BEGIN
                                    SET @sb =  @sb + @c; 
                              END 
                        END
                  END
                  --
            END
            SET @pos = @pos + 1;
      END   
      --
      -- Likely not needed.  This is handled above.
      --
      -- A final zed character will made sb = null,
      -- but otherwise we have an additional element.
      IF (@sb IS NOT NULL )
            INSERT @unpacked (element) VALUES (@sb);

      RETURN 
END
链接地址: http://www.djcxy.com/p/93862.html

上一篇: MySQL中的分层关系数据库设计

下一篇: SQL字符串[]打包/解压实用程序函数