【问题标题】:Sanitize a unicode string for logging清理用于记录的 unicode 字符串
【发布时间】:2019-11-25 11:21:36
【问题描述】:

在将数据写入日志文件之前,我正在使用以下规则编写字符串清理程序:

  1. 指定的字符被列入白名单(A-Za-z0-9 以及 <>[],.:_- 和空格)
  2. 指定的字符在三角括号内被转换成英文版本的名字(例如"," => "<comma>""%" => "<percent>"
  3. 任何其他内容都将转换为其在三角括号内的 unicode 编号(例如 "φ" => "<U+03C6>""π" => "<U+03C0>"

到目前为止 1 和 2 都在工作,但不是 3。这是我目前所拥有的:

    public static string Safe(string s)
    {
        s = s
            .Replace("<", "ooopen-angle-brackettt") // must come first
            .Replace(">", "ccclose-angle-brackettt") // must come first
            //.Replace(",", "<comma>") // allow
            //.Replace(".", "<dot>") // allow
            //.Replace(":", "<colon>") // allow
            .Replace(";", "<semi-colon>")
            .Replace("{", "<open-curly-bracket>")
            .Replace("}", "<close-curly-bracket>")
            //.Replace("[", "<open-square-bracket>") // allow
            //.Replace("]", "<close-square-bracket>") // allow
            .Replace("(", "<open-bracket>")
            .Replace(")", "<close-bracket>")
            .Replace("!", "<exclamation-mark>")
            .Replace("@", "<at>")
            .Replace("#", "<hash>")
            .Replace("$", "<dollar>")
            .Replace("%", "<percent>")
            .Replace("^", "<hat>")
            .Replace("&", "<and>")
            .Replace("*", "<asterisk>")
            //.Replace("-", "<dash>") // allow
            //.Replace("_", "<underscore>") // allow
            .Replace("+", "<plus>")
            .Replace("=", "<equals>")
            .Replace("\\", "<forward-slash>")
            .Replace("\"", "<double-quote>")
            .Replace("'", "<single-quote>")
            .Replace("/", "<forward-slash>")
            .Replace("?", "<question-mark>")
            .Replace("|", "<pipe>")
            .Replace("~", "<tilde>")
            .Replace("`", "<backtick>")
            .Replace("ooopen-angle-brackettt", "<open-angle-bracket>")
            .Replace("ccclose-angle-brackettt", "<close-angle-bracket>");
        // all working upto here. broken below:

        Regex itemRegex = new Regex(@"[^A-Za-z0-9<>[\]:.,_\s-]", RegexOptions.Compiled);
        foreach (Match itemMatch in itemRegex.Matches(s))
        {
            // the reason for [0] and [1] is that I read that unicode consists of 2 characters
            s = s.Replace(
                itemMatch.ToString(),
                "<U+" +
                    (((int)(itemMatch.ToString()).ToCharArray()[0]).ToString("X4")).ToString() +
                    (((int)(itemMatch.ToString()).ToCharArray()[1]).ToString("X4")).ToString() +
                ">"
            );
        }
        return s;
    }

正则表达式部分未捕获输入字符串中的 unicode 字符。我该如何解决这个问题

【问题讨论】:

  • 你应该转义][^A-Za-z0-9&lt;&gt;[\]:.,_\s-]{1}是多余的,去掉它。
  • @WiktorStribiżew 谢谢我会更新这个问题。请注意,这并不能解决问题。
  • 你说“不捕获unicode字符”,你能提供一个测试用例吗?我认为您正在处理表情符号。
  • @WiktorStribiżew 请按照问题的第 3 部分使用 φ 和 π
  • 所有这些卫生设施的目的是什么?非 ASCII 字符有什么不安全的地方?为什么不写 UTF-8 日志文件?

标签: c# regex unicode-string


【解决方案1】:

问题是我假设 C# string 中存在的单个 unicode 值在该字符串转换为 char 数组 (char[]) 时会转换为多个项目。如果您将鼠标悬停在 Visual Studio 中的 stringchar 类型上,那么它实际上会告诉您这些类型与 unicode 的关系:

  • string:将文本表示为一系列 unicode 字符
  • char:将字符表示为 UTF-16 代码单元

这意味着 C# 字符串中的每个“字母”(即字符)实际上是一个 unicode char,因此当您将字符串转换为 char 数组时,该数组的每个项目现在都包含 1 unicode 字符。

还有一个缺失的难题:我们如何知道Regex.Match() 一次对 1 个 unicode 字符进行操作?它使用 UTF-16 还是 UTF-32?对于这个问题的答案我looked up the documentation

\unnnn - 使用十六进制表示匹配 Unicode 字符(正好四位,由 nnnn 表示)。

所以 C# 正则表达式支持 UTF-16(2 个字节),但不支持 UTF-32。像 .{1} 这样的模式将准确捕获 1 个 UTF-16 字符。

那么解决方案就是不要尝试在原始问题中从itemMatch.ToString().ToCharArray() 中获取 2 个项目 - 因为那里只有 1 个项目!这是规则 3 缺少的解决方案(我坚持的部分):

        Regex itemRegex = new Regex(@"[^A-Za-z0-9<>[\]:\.,_\s-]", RegexOptions.Compiled); // {1} is implied

        foreach (Match itemMatch in itemRegex.Matches(s))
        {
            char unicodeChar = itemMatch.ToString().ToCharArray()[0]; // 1 char = 16 bits
            int unicodeNumber = (int)unicodeChar;
            string unicodeHex = unicodeNumber.ToString("X4");
            s = s.Replace(itemMatch.ToString(), "<U+" + unicodeHex + ">");
        }
        return s;

【讨论】:

  • 您可以删除循环体的前三行,然后使用 $"&lt;U+{(int)itemMatch.ToString()[0]:X4}&gt;"(或者 String.Format(...) 而不是 $ 对于旧 C#。
  • 其实你甚至不需要循环,只需要一行,s = itemRegex.Replace(s, m =&gt; $"&lt;U+{(int)m.Value[0]:X4}&gt;");
  • 另外,如果您使用RegexOptions.Compiled,请确保itemRegex 对象只创建一次并且每次都使用。否则你不会得到更好的表现。很抱歉给您发送垃圾评论。
  • @Dialectus,我可以,但是使用断点进行调试会变得不那么可读并且更难。我是故意这样做的。感谢您提供有关RegexOptions.Compiled tho 的提示。
猜你喜欢
  • 2014-05-22
  • 2020-07-02
  • 2017-09-25
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多