Как вы можете удалить не-ASCII символы из строки? (в C#)
Как вы можете удалить не-ASCII символы из строки? (в C#)
17 ответов
string s = "søme string";
s = Regex.Replace(s, @"[^\u0000-\u007F]+", string.Empty);
Вот чистое решение.NET, которое не использует регулярные выражения:
string inputString = "Räksmörgås";
string asAscii = Encoding.ASCII.GetString(
Encoding.Convert(
Encoding.UTF8,
Encoding.GetEncoding(
Encoding.ASCII.EncodingName,
new EncoderReplacementFallback(string.Empty),
new DecoderExceptionFallback()
),
Encoding.UTF8.GetBytes(inputString)
)
);
Это может выглядеть громоздким, но это должно быть интуитивно понятно. Для преобразования строки используется кодировка.NET ASCII. UTF8 используется во время преобразования, потому что он может представлять любой из оригинальных символов. Он использует EncoderReplacementFallback для преобразования любого не-ASCII символа в пустую строку.
Я считаю, что MonsCamus имел в виду:
parsememo = Regex.Replace(parsememo, @"[^\u0020-\u007E]", string.Empty);
Если вы хотите не вырезать, а на самом деле конвертировать символы латинского акцентирования в символы без акцента, взгляните на этот вопрос: Как я могу перевести 8-битные символы в 7-битные символы? (то есть от Ü до U)
Вдохновленный решением регулярных выражений philcruz, я создал чистое решение LINQ
public static string PureAscii(this string source, char nil = ' ')
{
var min = '\u0000';
var max = '\u007F';
return source.Select(c => c < min ? nil : c > max ? nil : c).ToText();
}
public static string ToText(this IEnumerable<char> source)
{
var buffer = new StringBuilder();
foreach (var c in source)
buffer.Append(c);
return buffer.ToString();
}
Это непроверенный код.
Нет необходимости в регулярных выражениях. просто используйте кодировку...
sOutput = System.Text.Encoding.ASCII.GetString(System.Text.Encoding.ASCII.GetBytes(sInput));
Я обнаружил, что следующий слегка измененный диапазон полезен для разбора блоков комментариев из базы данных, это означает, что вам не придется бороться с символами табуляции и экранирования, которые могут привести к нарушению поля CSV.
parsememo = Regex.Replace(parsememo, @"[^\u001F-\u007F]", string.Empty);
Если вы хотите избежать использования других специальных символов или знаков пунктуации, проверьте таблицу ascii.
Я пришел сюда в поисках решения для расширенных символов ascii, но не смог его найти. Самое близкое, что я нашел, - это решение bzlm. Но это работает только для кода ASCII до 127(очевидно, вы можете заменить тип кодирования в его коде, но я думаю, что это было немного сложно понять. Следовательно, поделиться этой версией). Вот решение, которое работает для расширенных кодов ASCII, т.е. до 255, что является ISO 8859-1
Он находит и удаляет символы не-ascii (больше 255)
Dim str1 as String= "â, ??î or ôu� n☁i✑++$-♓!‼⁉4⃣od;/⏬'®;☕:☝)///1!@#"
Dim extendedAscii As Encoding = Encoding.GetEncoding("ISO-8859-1",
New EncoderReplacementFallback(String.empty),
New DecoderReplacementFallback())
Dim extendedAsciiBytes() As Byte = extendedAscii.GetBytes(str1)
Dim str2 As String = extendedAscii.GetString(extendedAsciiBytes)
console.WriteLine(str2)
'Output : â, ??î or ôu ni++$-!‼⁉4od;/';:)///1!@#$%^yz:
Замените кодировку согласно требованию, остальное должно остаться прежним.
Это не оптимальная производительность, а довольно простой подход Linq:
string strippedString = new string(
yourString.Where(c => c <= sbyte.MaxValue).ToArray()
);
Недостатком является то, что все "выжившие" символы сначала помещаются в массив типа char[]
который затем выбрасывается после string
конструктор больше не использует его.
public string ReturnCleanASCII(string s)
{
StringBuilder sb = new StringBuilder(s.Length);
foreach (char c in s)
{
if ((int)c > 127) // you probably don't want 127 either
continue;
if ((int)c < 32) // I bet you don't want control characters
continue;
if (c == '%')
continue;
if (c == '?')
continue;
sb.Append(c);
}
return sb.ToString();
}
Я провел небольшое тестирование, и ответ @bzlm - самый быстрый правильный ответ. Но оказывается, мы можем сделать гораздо быстрее. Преобразование с использованием кодирования эквивалентно следующему коду при встраиванииEncoding.Convert
public static string StripUnicode(string unicode) {
Encoding dstEncoding = GreedyAscii;
Encoding srcEncoding = Encoding.UTF8;
return dstEncoding.GetString(dstEncoding.GetBytes(srcEncoding.GetChars(srcEncoding.GetBytes(unicode))));
}
Как вы можете ясно видеть, мы выполняем два избыточных действия, перекодируя UTF8. Почему это вы можете спросить? C# хранит строки исключительно в графмемах UTF16. Это также могут быть графмемы UTF8, поскольку Unicode взаимосовместим. (Примечание: решение @bzlm разбивает символы UTF16, что может вызвать исключение во время транскодирования.) => Операция не зависит от исходной кодировки, поскольку она всегда UTF16.
Давайте избавимся от избыточного перекодирования и предотвратим сбои в пограничных случаях.
public static string StripUnicode(string unicode) {
Encoding dstEncoding = GreedyAscii;
return dstEncoding.GetString(dstEncoding.GetBytes(unicode));
}
У нас уже есть упрощенное и вполне работоспособное решение. Что требует менее половины времени для вычислений.
Прирост производительности невелик, но для дальнейшей оптимизации памяти мы можем сделать две вещи:
- Принять
ReadOnlySpan<char>
для более удобного API. - Попытка соответствовать временному
byte[]
в стек; в противном случае используйте пул массивов.
public static string StripUnicode(ReadOnlySpan<char> unicode) {
return EnsureEncoding(unicode, GreedyAscii);
}
/// <summary>Produces a string which is compatible with the limiting encoding</summary>
/// <remarks>Ensure that the encoding does not throw on illegal characters</remarks>
public static string EnsureEncoding(ReadOnlySpan<char> unicode, Encoding limitEncoding) {
int asciiBytesLength = limitEncoding.GetMaxByteCount(unicode.Length);
byte[]? asciiBytes = asciiBytesLength <= 2048 ? null : ArrayPool<byte>.Shared.Rent(asciiBytesLength);
Span<byte> asciiSpan = asciiBytes ?? stackalloc byte[asciiBytesLength];
asciiBytesLength = limitEncoding.GetBytes(unicode, asciiSpan);
asciiSpan = asciiSpan.Slice(0, asciiBytesLength);
string asciiChars = limitEncoding.GetString(asciiSpan);
if (asciiBytes is { }) {
ArrayPool<byte>.Shared.Return(asciiBytes);
}
return asciiChars;
}
private static Encoding GreedyAscii { get; } = Encoding.GetEncoding(Encoding.ASCII.EncodingName, new EncoderReplacementFallback(string.Empty), new DecoderExceptionFallback());
Вы можете увидеть это в действии на сайте sharplab.io .
Я использую это регулярное выражение для фильтрации плохих символов в имени файла.
Regex.Replace(directory, "[^a-zA-Z0-9\\:_\- ]", "")
Это должны быть все символы, разрешенные для имен файлов.
Я использовал это регулярное выражение:
string s = "søme string";
Regex regex = new Regex(@"[^a-zA-Z0-9\s]", (RegexOptions)0);
return regex.Replace(s, "");
Если вам нужна строка, содержащая только символы ISO-8859-1 и исключающая нестандартные символы, вы должны использовать это выражение:
var result = Regex.Replace(value, @"[^\u0020-\u007E\u00A0-\u00FF]+", string.Empty);
Примечание. Использование метода Encoding.GetEncoding("ISO-8859-1") не поможет, поскольку неопределенные символы не исключаются.
Кодовая страница Википедии ISO-8859-1 для более подробной информации.
Вы можете использоватьChar.IsAscii
чтобы определить персонажей, которых вы хотите сохранить. Простая реализация может выглядеть так:
public static string StripNonAscii(this string input)
{
StringBuilder resultBuilder = new();
foreach (char character in input)
if (char.IsAscii(character))
resultBuilder.Append(character);
return resultBuilder.ToString();
}
Некромантия.
Кроме того, метод bzlm можно использовать для удаления символов, которые не входят в произвольную кодировку, а не только в ASCII:
// https://en.wikipedia.org/wiki/Code_page#EBCDIC-based_code_pages
// https://en.wikipedia.org/wiki/Windows_code_page#East_Asian_multi-byte_code_pages
// https://en.wikipedia.org/wiki/Chinese_character_encoding
System.Text.Encoding encRemoveAllBut = System.Text.Encoding.ASCII;
encRemoveAllBut = System.Text.Encoding.GetEncoding(System.Globalization.CultureInfo.InstalledUICulture.TextInfo.ANSICodePage); // System-encoding
encRemoveAllBut = System.Text.Encoding.GetEncoding(1252); // Western European (iso-8859-1)
encRemoveAllBut = System.Text.Encoding.GetEncoding(1251); // Windows-1251/KOI8-R
encRemoveAllBut = System.Text.Encoding.GetEncoding("ISO-8859-5"); // used by less than 0.1% of websites
encRemoveAllBut = System.Text.Encoding.GetEncoding(37); // IBM EBCDIC US-Canada
encRemoveAllBut = System.Text.Encoding.GetEncoding(500); // IBM EBCDIC Latin 1
encRemoveAllBut = System.Text.Encoding.GetEncoding(936); // Chinese Simplified
encRemoveAllBut = System.Text.Encoding.GetEncoding(950); // Chinese Traditional
encRemoveAllBut = System.Text.Encoding.ASCII; // putting ASCII again, as to answer the question
// https://stackoverflow.com/questions/123336/how-can-you-strip-non-ascii-characters-from-a-string-in-c
string inputString = "RäksmörПривет, мирgås";
string asAscii = encRemoveAllBut.GetString(
System.Text.Encoding.Convert(
System.Text.Encoding.UTF8,
System.Text.Encoding.GetEncoding(
encRemoveAllBut.CodePage,
new System.Text.EncoderReplacementFallback(string.Empty),
new System.Text.DecoderExceptionFallback()
),
System.Text.Encoding.UTF8.GetBytes(inputString)
)
);
System.Console.WriteLine(asAscii);
И для тех, кто просто хочет удалить акценты:
(осторожно, потому что нормализовать != латинизировать != романизировать)
// string str = Latinize("(æøå âôû?aè");
public static string Latinize(string stIn)
{
// Special treatment for German Umlauts
stIn = stIn.Replace("ä", "ae");
stIn = stIn.Replace("ö", "oe");
stIn = stIn.Replace("ü", "ue");
stIn = stIn.Replace("Ä", "Ae");
stIn = stIn.Replace("Ö", "Oe");
stIn = stIn.Replace("Ü", "Ue");
// End special treatment for German Umlauts
string stFormD = stIn.Normalize(System.Text.NormalizationForm.FormD);
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int ich = 0; ich < stFormD.Length; ich++)
{
System.Globalization.UnicodeCategory uc = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);
if (uc != System.Globalization.UnicodeCategory.NonSpacingMark)
{
sb.Append(stFormD[ich]);
} // End if (uc != System.Globalization.UnicodeCategory.NonSpacingMark)
} // Next ich
//return (sb.ToString().Normalize(System.Text.NormalizationForm.FormC));
return (sb.ToString().Normalize(System.Text.NormalizationForm.FormKC));
} // End Function Latinize