Перетворіть рядок Unicode в рядок ASCII


78

Як я можу перетворити цей рядок:

This string contains the Unicode character Pi(π)

у зниклий рядок ASCII:

This string contains the Unicode character Pi(\u03a0)

і навпаки ?

Поточне кодування, доступне в C #, перетворює символ π на "?". Мені потрібно зберегти цей характер.

Відповіді:


136

Це рухається вперед і назад у форматі \ uXXXX.

class Program {
    static void Main( string[] args ) {
        string unicodeString = "This function contains a unicode character pi (\u03a0)";

        Console.WriteLine( unicodeString );

        string encoded = EncodeNonAsciiCharacters(unicodeString);
        Console.WriteLine( encoded );

        string decoded = DecodeEncodedNonAsciiCharacters( encoded );
        Console.WriteLine( decoded );
    }

    static string EncodeNonAsciiCharacters( string value ) {
        StringBuilder sb = new StringBuilder();
        foreach( char c in value ) {
            if( c > 127 ) {
                // This character is too big for ASCII
                string encodedValue = "\\u" + ((int) c).ToString( "x4" );
                sb.Append( encodedValue );
            }
            else {
                sb.Append( c );
            }
        }
        return sb.ToString();
    }

    static string DecodeEncodedNonAsciiCharacters( string value ) {
        return Regex.Replace(
            value,
            @"\\u(?<Value>[a-zA-Z0-9]{4})",
            m => {
                return ((char) int.Parse( m.Groups["Value"].Value, NumberStyles.HexNumber )).ToString();
            } );
    }
}

Виходи:

Ця функція містить символ унікоду pi (π)

Ця функція містить символ юнікоду pi (\ u03a0)

Ця функція містить символ унікоду pi (π)


1
DecodeEncodedNonAsciiCharacters викине FormatException для рядків типу "\\ user"
vovafeldman

3
\ користувач не повинен збігатися, тому що після u немає 4 символів, але я розумію вашу думку. Просто змініть відповідність символу регулярного виразу на [a-fA-F0-9]. Він все одно буде відповідати речам, яким він не призначений, але, схоже, він все ще відповідає наміру вихідного запитання.
Адам Сіллс

2
Виглядає красиво і чисто. Тим не менш, я здивований, що не існує класу System .Net, який би це зробив.
saarp

Не зовсім впевнений, чому для цього існує клас System. Не всі мови використовують послідовності символів екранування (наприклад, VB.NET не використовує). Тож це було б специфічно для мови. Можливо, ви зможете використовувати Microsoft.CSharp.CSharpCodeProvider, щоб зробити це, але це здається надмірним.
Адам Сіллс

3
@AdamSills, якщо сторонній сервер їх повертає, їм буде потрібно декодування. Статичний метод був би непоганий у класі Net або Web для випадків, коли ви хочете перетворити ці символи.
Джеймс Джеффрі,

22

Для Unescape Ви можете просто використовувати ці функції:

System.Text.RegularExpressions.Regex.Unescape(string)

System.Uri.UnescapeDataString(string)

Я пропоную використовувати цей метод (Він краще працює з UTF-8):

UnescapeDataString(string)

1
У моєму випадку за допомогою "System.Text.RegularExpressions.Regex.Unescape" я отримав кращий результат.
Хосро

11
string StringFold(string input, Func<char, string> proc)
{
  return string.Concat(input.Select(proc).ToArray());
}

string FoldProc(char input)
{
  if (input >= 128)
  {
    return string.Format(@"\u{0:x4}", (int)input);
  }
  return input.ToString();
}

string EscapeToAscii(string input)
{
  return StringFold(input, FoldProc);
}

4

Як однокласний:

var result = Regex.Replace(input, @"[^\x00-\x7F]", c => 
    string.Format(@"\u{0:x4}", (int)c.Value[0]));

2
class Program
{
        static void Main(string[] args)
        {
            char[] originalString = "This string contains the unicode character Pi(π)".ToCharArray();
            StringBuilder asAscii = new StringBuilder(); // store final ascii string and Unicode points
            foreach (char c in originalString)
            {
                // test if char is ascii, otherwise convert to Unicode Code Point
                int cint = Convert.ToInt32(c);
                if (cint <= 127 && cint >= 0)
                    asAscii.Append(c);
                else
                    asAscii.Append(String.Format("\\u{0:x4} ", cint).Trim());
            }
            Console.WriteLine("Final string: {0}", asAscii);
            Console.ReadKey();
        }
}

Усі символи, що не належать до ASCII, перетворюються у своє представлення Unicode Code Point і додаються до останнього рядка.


1

Невеликий патч до відповіді @Adam Sills, який вирішує FormatExceptionвипадки, коли вхідний рядок типу "c: \ u00ab \ otherdirectory \ " плюс RegexOptions.Compiledробить Regexкомпіляцію набагато швидшою:

    private static Regex DECODING_REGEX = new Regex(@"\\u(?<Value>[a-fA-F0-9]{4})", RegexOptions.Compiled);
    private const string PLACEHOLDER = @"#!#";
    public static string DecodeEncodedNonAsciiCharacters(this string value)
    {
        return DECODING_REGEX.Replace(
            value.Replace(@"\\", PLACEHOLDER),
            m => { 
                return ((char)int.Parse(m.Groups["Value"].Value, NumberStyles.HexNumber)).ToString(); })
            .Replace(PLACEHOLDER, @"\\");
    }

1

Ось моя поточна реалізація:

public static class UnicodeStringExtensions
{
    public static string EncodeNonAsciiCharacters(this string value) {
        var bytes = Encoding.Unicode.GetBytes(value);
        var sb = StringBuilderCache.Acquire(value.Length);
        bool encodedsomething = false;
        for (int i = 0; i < bytes.Length; i += 2) {
            var c = BitConverter.ToUInt16(bytes, i);
            if ((c >= 0x20 && c <= 0x7f) || c == 0x0A || c == 0x0D) {
                sb.Append((char) c);
            } else {
                sb.Append($"\\u{c:x4}");
                encodedsomething = true;
            }
        }
        if (!encodedsomething) {
            StringBuilderCache.Release(sb);
            return value;
        }
        return StringBuilderCache.GetStringAndRelease(sb);
    }


    public static string DecodeEncodedNonAsciiCharacters(this string value)
      => Regex.Replace(value,/*language=regexp*/@"(?:\\u[a-fA-F0-9]{4})+", Decode);

    static readonly string[] Splitsequence = new [] { "\\u" };
    private static string Decode(Match m) {
        var bytes = m.Value.Split(Splitsequence, StringSplitOptions.RemoveEmptyEntries)
                .Select(s => ushort.Parse(s, NumberStyles.HexNumber)).SelectMany(BitConverter.GetBytes).ToArray();
        return Encoding.Unicode.GetString(bytes);
    }
}

Це проходить тест:

public void TestBigUnicode() {
    var s = "\U00020000";
    var encoded = s.EncodeNonAsciiCharacters();
    var decoded = encoded.DecodeEncodedNonAsciiCharacters();
    Assert.Equals(s, decoded);
}

із закодованим значенням: "\ud840\udc00"

Ця реалізація використовує StringBuilderCache (посилання на джерело посилання)


0

Вам потрібно використовувати Convert()метод у Encodingкласі:

  • Створіть Encodingоб'єкт, який представляє кодування ASCII
  • Створіть Encodingоб’єкт, який представляє кодування Unicode
  • Телефонуйте Encoding.Convert()із кодом джерела, кодом призначення та рядком, що кодується

Існує приклад тут :

using System;
using System.Text;

namespace ConvertExample
{
   class ConvertExampleClass
   {
      static void Main()
      {
         string unicodeString = "This string contains the unicode character Pi(\u03a0)";

         // Create two different encodings.
         Encoding ascii = Encoding.ASCII;
         Encoding unicode = Encoding.Unicode;

         // Convert the string into a byte[].
         byte[] unicodeBytes = unicode.GetBytes(unicodeString);

         // Perform the conversion from one encoding to the other.
         byte[] asciiBytes = Encoding.Convert(unicode, ascii, unicodeBytes);

         // Convert the new byte[] into a char[] and then into a string.
         // This is a slightly different approach to converting to illustrate
         // the use of GetCharCount/GetChars.
         char[] asciiChars = new char[ascii.GetCharCount(asciiBytes, 0, asciiBytes.Length)];
         ascii.GetChars(asciiBytes, 0, asciiBytes.Length, asciiChars, 0);
         string asciiString = new string(asciiChars);

         // Display the strings created before and after the conversion.
         Console.WriteLine("Original string: {0}", unicodeString);
         Console.WriteLine("Ascii converted string: {0}", asciiString);
      }
   }
}

4
Я вже спробував це. Проблема з ним полягає в тому, що він перетворює символ юнікоду π (\ u03a0) у "?". Мені потрібно, щоб перетворити його на "\ u03a0".
Алі

0

Для збереження фактичних кодових точок Unicode потрібно спочатку розшифрувати кодові одиниці UTF-16 рядка до кодових одиниць UTF-32 (які в даний час збігаються з кодовими точками Unicode). Використовуйте System.Text.Encoding.UTF32.GetBytes()для цього, а потім запишіть отримані байти в те StringBuilder, що потрібно, тобто

static void Main(string[] args) 
{ 
    String originalString = "This string contains the unicode character Pi(π)"; 
    Byte[] bytes = Encoding.UTF32.GetBytes(originalString);
    StringBuilder asAscii = new StringBuilder();
    for (int idx = 0; idx < bytes.Length; idx += 4)
    { 
        uint codepoint = BitConverter.ToUInt32(bytes, idx);
        if (codepoint <= 127) 
            asAscii.Append(Convert.ToChar(codepoint)); 
        else 
            asAscii.AppendFormat("\\u{0:x4}", codepoint); 
    } 
    Console.WriteLine("Final string: {0}", asAscii); 
    Console.ReadKey(); 
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.