【问题标题】:Removing hidden characters from within strings从字符串中删除隐藏字符
【发布时间】:2013-02-21 22:45:10
【问题描述】:

我的问题:

我有一个 .NET 应用程序,它通过电子邮件发送时事通讯。在 Outlook 中查看时事通讯时,Outlook 会显示一个问号来代替它无法识别的隐藏字符。这些隐藏字符来自最终用户,他们将组成时事通讯的 html 复制并粘贴到表单中并提交。如果这些隐藏字符出现在字符串的末尾或开头,则 c# trim() 会删除它们。当在 gmail 中查看时事通讯时,gmail 可以很好地忽略它们。将这些隐藏字符粘贴到 Word 文档中并打开“显示段落标记和隐藏符号”选项时,符号显示为一个更大矩形内的一个矩形。此外,组成时事通讯的文本可以是任何语言,因此必须接受 Unicode 字符。我尝试遍历字符串以检测字符,但循环无法识别它并通过它。还要求最终用户在提交之前先将 html 粘贴到记事本中是不可能的。

我的问题:
如何使用 C# 检测和消除这些隐藏字符?

【问题讨论】:

  • 这里举个例子..
  • 示例无效值会很好。我在 ascii 文本中猜测它的 unicode 字符串,但这只是一个猜测。
  • 正则表达式,只允许字母和数字
  • 我不知道隐藏的字符是什么。它只在outlook或word中显示一次。如果我在 SharePoint 列表(存储它的位置)中查看文本,它会被隐藏。

标签: c# .net string hidden-characters


【解决方案1】:

您可以使用以下方式从输入字符串中删除所有控制字符:

string input; // this is your input string
string output = new string(input.Where(c => !char.IsControl(c)).ToArray());

Here is the documentation 用于 IsControl() 方法。

或者如果你只想保留字母和数字,你也可以使用IsLetterIsDigit函数:

string output = new string(input.Where(c => char.IsLetter(c) || char.IsDigit(c)).ToArray());

【讨论】:

  • 谢谢,我会试试这个。我将尝试对其进行编码并立即将其解码,以查看隐藏的字符是否被剥离。
  • HtmlEncode/Decode 不会删除任何字符,不知道您建议如何使用它。
  • 我不知道为什么,但是 Char.IsControl 对于从左到右的标记返回 false
  • @YannickBlondeau 也会删除标点符号和特殊字符“£$%^”等,所以在我看来,最好的解决方案是 2 的组合,或者我添加的答案
  • @IgorMeszaros LRM 是一个“格式”字符,但幸运的是 C# 有一个 GetUnicodeCategory(char c) 方法可以识别任何字符的类别。 string clean = new string(e.Value.Where(c => char.GetUnicodeCategory(c) != UnicodeCategory.Format).ToArray()); 可以很好地删除 LRM。
【解决方案2】:

你可以这样做:

var hChars = new char[] {...};
var result = new string(yourString.Where(c => !hChars.Contains(c)).ToArray());

【讨论】:

    【解决方案3】:

    我通常使用这个正则表达式来替换所有不可打印的字符。

    顺便说一句,大多数人认为制表符、换行符和回车符是不可打印的字符,但对我来说不是。

    所以这是表达式:

    string output = Regex.Replace(input, @"[^\u0009\u000A\u000D\u0020-\u007E]", "*");
    
    • ^ 表示是否为以下任何一种:
    • \u0009 是标签
    • \u000A 是换行符
    • \u000D是回车
    • \u0020-\u007E 表示从空间到 ~ 的所有内容——即 ASCII 格式的所有内容。

    如果您想进行更改,请参阅ASCII table。请记住,它会删除每个非 ASCII 字符。

    要进行上述测试,您可以像这样自己创建一个字符串:

        string input = string.Empty;
    
        for (int i = 0; i < 255; i++)
        {
            input += (char)(i);
        }
    

    【讨论】:

    • 我认为第一个 ^ 反转了集合,而其他 ^s 不应该存在(将从输出中排除 ^)。
    【解决方案4】:
    string output = new string(input.Where(c => !char.IsControl(c)).ToArray());
    

    这肯定会解决问题。我的字符串中有一个不可打印的替代字符(ASCII 26),导致我的应用程序中断,这行代码删除了字符

    【讨论】:

    【解决方案5】:

    最适合我的是:

    string result = new string(value.Where(c =>  char.IsLetterOrDigit(c) || (c >= ' ' && c <= byte.MaxValue)).ToArray());
    

    我要确保字符是任何字母或数字,这样我就不会忽略任何非英文字母,或者如果它不是字母,我会检查它是否是大于或等于空格的 ascii 字符确保我忽略了一些控制字符,这样可以确保我不会忽略标点符号。

    有些人建议使用 IsControl 来检查字符是否不可打印,但例如忽略从左到右标记。

    【讨论】:

      【解决方案6】:
      new string(input.Where(c => !char.IsControl(c)).ToArray());
      

      IsControl 遗漏了一些控制字符,例如从左到右标记 (LRM)(在进行复制粘贴时通常隐藏在字符串中的字符)。如果您确定您的字符串只有数字和数字,那么您可以使用 IsLetterOrDigit

      new string(input.Where(c => char.IsLetterOrDigit(c)).ToArray())
      

      如果你的字符串有特殊字符,那么

      new string(input.Where(c => c < 128).ToArray())
      

      【讨论】:

      • 不幸的是,根据我的单元测试,最后一个建议 (new string(input.Where(c =&gt; c &lt; 128).ToArray())) 也会去掉重音字符。例如,“Siñalizacíon”将变为“Sializacon”。
      【解决方案7】:

      我使用这个快速而肮脏的 oneliner 从损坏的 Windows 10 计算器应用程序留下的 LTR/RTL 标记中清除了一些输入。它可能与完美相去甚远,但足以快速修复:

      string cleaned = new string(input.Where(c => !char.IsControl(c) && (char.IsLetterOrDigit(c) || char.IsPunctuation(c) || char.IsSeparator(c) || char.IsSymbol(c) || char.IsWhiteSpace(c))).ToArray());
      

      【讨论】:

        【解决方案8】:

        TLDR 答案

        使用这个正则表达式...

        \P{Cc}\P{Cn}\P{Cs}
        

        像这样……

        var regex = new Regex(@"![\P{Cc}\P{Cn}\P{Cs}]");
        

        TLDR 说明

        • \P{Cc}不要匹配控制字符。
        • \P{Cn}不要匹配未分配的字符。
        • \P{Cs}不要匹配 UTF-8 无效字符。

        工作演示

        在这个演示中,我使用这个正则表达式来搜索字符串"Hello, World!"。最后那个奇怪的字符是(char)4——这是END TRANSMISSION的字符。

        using System;
        using System.Text.RegularExpressions;
        
        public class Test {
            public static void Main() {
                var regex = new Regex(@"![\P{Cc}\P{Cn}\P{Cs}]");
                var matches = regex.Matches("Hello, World!" + (char)4);
                Console.WriteLine("Results: " + matches.Count);
                foreach (Match match in matches) {
                    Console.WriteLine("Result: " + match);
                }
            }
        }
        

        Full Working Demo at IDEOne.com

        上述代码的输出:

        Results: 1
        Result: !
        

        替代品

        • \P{C} :仅匹配可见字符。不要匹配任何不可见的字符。
        • \P{Cc} :仅匹配非控制字符。不要匹配任何控制字符。
        • \P{Cc}\P{Cn} :仅匹配已分配的非控制字符。不要匹配任何控制或未分配的字符。
        • \P{Cc}\P{Cn}\P{Cs} :仅匹配已分配且 UTF-8 有效的非控制字符。不匹配任何控制、未分配或 UTF-8 无效字符。
        • \P{Cc}\P{Cn}\P{Cs}\P{Cf} :仅匹配已分配且 UTF-8 有效的非控制、非格式化字符。不匹配任何控制、未分配、格式化或 UTF-8 无效字符。

        来源及说明

        查看可用于在正则表达式中进行测试的Unicode Character Properties。您应该能够在Microsoft .NETJavaScriptPythonJavaPHPRubyPerlGolang 甚至Adobe 中使用这些正则表达式。了解 Unicode 字符类是非常容易转移的知识,所以我推荐使用它!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-01-17
          • 1970-01-01
          • 2020-05-04
          • 1970-01-01
          • 2011-11-01
          • 2016-05-04
          • 2014-08-06
          • 1970-01-01
          相关资源
          最近更新 更多