Удаление скрытых символов из строк

Моя проблема:

У меня есть приложение.NET, которое рассылает информационные бюллетени по электронной почте. Когда информационные бюллетени просматриваются в outlook, outlook отображает знак вопроса вместо скрытого символа, который он не может распознать. Эти скрытые символы исходят от конечных пользователей, которые копируют и вставляют html, который составляет информационные бюллетени в форму и отправляет их. C# trim() удаляет эти скрытые символы, если они встречаются в конце или начале строки. Когда новостная рассылка просматривается в gmail, gmail хорошо игнорирует их. При вставке этих скрытых символов в документ Word и включении параметра "Показывать метки абзаца и скрытые символы" символы отображаются в виде одного прямоугольника внутри большего прямоугольника. Кроме того, текст, из которого состоят информационные бюллетени, может быть на любом языке, поэтому принятие символов Unicode является обязательным. Я попытался перебрать строку, чтобы обнаружить символ, но цикл не распознает его и проходит по нему. Также просить конечного пользователя вставить html в блокнот, прежде чем отправлять его, не может быть и речи.

Мой вопрос:
Как я могу обнаружить и устранить эти скрытые символы, используя C#?

12 ответов

Решение

Вы можете удалить все управляющие символы из входной строки следующим образом:

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

Вот документация для IsControl() метод.

Или, если вы хотите сохранить только буквы и цифры, вы также можете использовать IsLetter а также IsDigit функция:

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

Я обычно использую это регулярное выражение для замены всех непечатаемых символов.

Кстати, большинство людей думают, что табуляция, перевод строки и возврат каретки являются непечатными символами, но для меня это не так.

Итак, вот выражение:

string output = Regex.Replace(input, @"[^\u0009\u000A\u000D\u0020-\u007E]", "*");
  • ^ означает, что это одно из следующих:
  • \u0009 это вкладка
  • \u000A перевод строки
  • \u000D возврат каретки
  • \u0020-\u007E означает все от космоса до ~ - то есть все в ASCII.

Смотрите таблицу ASCII, если вы хотите внести изменения. Помните, что это скинет каждый не-ASCII символ.

Для проверки выше вы можете создать строку самостоятельно, например так:

    string input = string.Empty;

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

Что лучше всего сработало для меня:

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

Когда я проверяю, является ли символ любой буквой или цифрой, чтобы я не игнорировал любые неанглийские буквы, или, если это не буква, я проверяю, является ли это символ ascii, который больше или равен пробелу, чтобы убедиться, Я игнорирую некоторые управляющие символы, это гарантирует, что я не игнорирую знаки препинания.

Некоторые предлагают использовать IsControl, чтобы проверить, является ли символ непечатным или нет, но при этом игнорируется, например, метка слева направо.

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())

Вы можете сделать это:

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

Ответ TLDR

Используйте это регулярное выражение ...

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

Как это...

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

Объяснение TLDR

  • : Не совпадать с управляющими символами.
  • \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);
        }
    }
}

Полная рабочая демонстрация на 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, которые можно использовать для проверки в регулярном выражении. Вы должны уметь использовать эти регулярные выражения в Microsoft .NET, JavaScript, Python, Java, PHP, Ruby, Perl, Golang и даже Adobe . Знание классов символов Unicode - это очень полезное знание, поэтому я рекомендую его использовать!

Если вам нужна скорость, создайте статический метод, который выглядит следующим образом:

      private static string RemoveControlCharacters(ReadOnly<char> input)
{
    Span<char> output = stackalloc char[input.Length];
    int j = 0;

    foreach (char c in input)
    {
        if (!char.IsControl(c))
        {
            output[j++] = c;
        }
    }

    return new string(output.Slice(0, j));
}

Он использует stackalloc для выделения памяти для выходной строки в стеке, что быстрее, чем выделение кучи.

Если вы знаете, что это за символы, вы можете использовать string.Replace:

newString = oldString.Replace("?", "");

где "?" представляет персонажа, которого вы хотите вырезать.

Недостаток этого подхода заключается в том, что вам необходимо повторять этот вызов, если есть несколько символов, которые вы хотите удалить.

У меня возникла ошибка с AWS S3 SDK «Путь к целевому ресурсу [имя -‎3.‎30.‎2022 -‎15‎.‎27.‎00.pdf] имеет двунаправленные символы, которые не поддерживаются System.Uri и, следовательно, не может быть обработан пакетом SDK для .NET"

Имя файла в моем экземпляре содержало символ Unicode «ЗНАК СЛЕВА НАПРАВО» (U+200E) между точками. Они не были видны в html или в Notepad++. Когда текст был вставлен в редактор Visual Studio 2019, текст Unicode был виден, и мне удалось решить проблему.

Проблема решилась заменой всех управляющих и других непечатаемых символов из имени файла с помощью следующего скрипта.

      var input = Regex.Replace(s, @"\p{C}+", string.Empty);

Источник кредита: /questions/45066202/c-regulyarnoe-vyirazhenie-dlya-udaleniya-nepechataemyih-simvolov-i-upravlyayuschih-simvolov-v-tekste-kotoryij-sostoit-iz-mnozhestva-raznyih-yazyikov-bukv-yunikoda/45066212#45066212

Это было некоторое время, но это еще не ответили.

Как вы включаете содержимое HMTL в код отправки? если вы читаете его из файла, проверьте кодировку файла. Если вы используете UTF-8 с подписью (имя немного различается у разных редакторов), это может вызвать странный символ в начале письма.

Я использовал этот быстрый и грязный лайнер, чтобы очистить некоторые входные данные от меток LTR/RTL, оставленных неработающим калькулятором в Windows 10. Вероятно, это далеко не идеально, но достаточно для быстрого исправления:

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());

Строка output = новая строка (input.Where(c =>!char.IsControl(c)).ToArray()); Это, безусловно, решит проблему. У меня был непечатаемый символ замены (ASCII 26) в строке, которая приводила к разрыву моего приложения, и эта строка кода удаляла символы

Другие вопросы по тегам