9

次のプロパティを使用して、 2 つの関数escape(text, delimiter)を記述しようとしています。unescape(text, delimiter)

  1. の結果にescapeは が含まれていませんdelimiter

  2. unescapeの逆escape、つまり

    unescape(escape(text, delimiter), delimiter) == text
    

    と のすべての値textに対してdelimiter

の許容値を制限しても問題ありませんdelimiter


背景: 区切り記号で区切られた値の文字列を作成したいと考えています。文字列から同じリストを再度抽出できるようにするには、個々の区切られた文字列に区切り記号が含まれていないことを確認する必要があります。


私が試したこと:簡単な解決策(疑似コード)を思いつきました:

escape(text, delimiter):   return text.Replace("\", "\\").Replace(delimiter, "\d")
unescape(text, delimiter): return text.Replace("\d", delimiter).Replace("\\", "\")

しかし、プロパティ 2 がテスト文字列で失敗したことを発見しました"\d<delimiter>"。現在、私は次の実用的なソリューションを持っています

escape(text, delimiter):   return text.Replace("\", "\b").Replace(delimiter, "\d")
unescape(text, delimiter): return text.Replace("\d", delimiter).Replace("\b", "\")

、またはでdelimiterはない限り、これはうまくいくようです(これは問題ありませんが、とにかく区切り文字として使用したくありません)。ただし、その正しさを正式に証明していないため、プロパティの 1 つが違反されているケースを見落としている可能性があります。これは非常に一般的な問題であるため、これには「よく知られた証明済みの正しい」アルゴリズムが既にあると思います。したがって、私の質問です (タイトルを参照)。\bd

4

2 に答える 2

4

最初のアルゴリズムは正しいです。

エラーは unescape() の実装にあります。同じ pass で\dbydelimiter\\by\の両方を置き換える必要があります。このように Replace() を複数回呼び出すことはできません。

区切り文字で区切られた文字列を安全に引用するためのサンプル C# コードを次に示します。

    static string QuoteSeparator(string str,
        char separator, char quoteChar, char otherChar) // "~" -> "~~"     ";" -> "~s"
    {
        var sb = new StringBuilder(str.Length);
        foreach (char c in str)
        {
            if (c == quoteChar)
            {
                sb.Append(quoteChar);
                sb.Append(quoteChar);
            }
            else if (c == separator)
            {
                sb.Append(quoteChar);
                sb.Append(otherChar);
            }
            else
            {
                sb.Append(c);
            }
        }
        return sb.ToString(); // no separator in the result -> Join/Split is safe
    }
    static string UnquoteSeparator(string str,
        char separator, char quoteChar, char otherChar) // "~~" -> "~"     "~s" -> ";"
    {
        var sb = new StringBuilder(str.Length);
        bool isQuoted = false;
        foreach (char c in str)
        {
            if (isQuoted)
            {
                if (c == otherChar)
                    sb.Append(separator);
                else
                    sb.Append(c);
                isQuoted = false;
            }
            else
            {
                if (c == quoteChar)
                    isQuoted = true;
                else
                    sb.Append(c);
            }
        }
        if (isQuoted)
            throw new ArgumentException("input string is not correctly quoted");
        return sb.ToString(); // ";" are restored
    }

    /// <summary>
    /// Encodes the given strings as a single string.
    /// </summary>
    /// <param name="input">The strings.</param>
    /// <param name="separator">The separator.</param>
    /// <param name="quoteChar">The quote char.</param>
    /// <param name="otherChar">The other char.</param>
    /// <returns></returns>
    public static string QuoteAndJoin(this IEnumerable<string> input,
        char separator = ';', char quoteChar = '~', char otherChar = 's')
    {
        CommonHelper.CheckNullReference(input, "input");
        if (separator == quoteChar || quoteChar == otherChar || separator == otherChar)
            throw new ArgumentException("cannot quote: ambiguous format");
        return string.Join(new string(separator, 1), (from str in input select QuoteSeparator(str, separator, quoteChar, otherChar)).ToArray());
    }

    /// <summary>
    /// Decodes the strings encoded in a single string.
    /// </summary>
    /// <param name="encoded">The encoded.</param>
    /// <param name="separator">The separator.</param>
    /// <param name="quoteChar">The quote char.</param>
    /// <param name="otherChar">The other char.</param>
    /// <returns></returns>
    public static IEnumerable<string> SplitAndUnquote(this string encoded,
        char separator = ';', char quoteChar = '~', char otherChar = 's')
    {
        CommonHelper.CheckNullReference(encoded, "encoded");
        if (separator == quoteChar || quoteChar == otherChar || separator == otherChar)
            throw new ArgumentException("cannot unquote: ambiguous format");
        return from s in encoded.Split(separator) select UnquoteSeparator(s, separator, quoteChar, otherChar);
    }
于 2012-06-14T13:08:12.650 に答える
0

区切り文字が\,bまたはで始まる場合は、代替の代替品を用意できるかもしれませんdunescapeアルゴリズムでも同じ代替置換を使用します

于 2012-06-14T13:12:25.140 に答える