2009-10-23 18 views
59

¿Cómo puedo convertir esta cadena:Convertir una cadena Unicode en una cadena ASCII de escape

This string contains the Unicode character Pi(π) 

en una cadena ASCII de escape:

This string contains the Unicode character Pi(\u03a0) 

y viceversa ?

La codificación actual disponible en C# convierte el carácter π a "?". Necesito preservar ese personaje.

Respuesta

99

Esto va de un lado a otro hacia y desde el formato \ 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(); 
      }); 
    } 
} 

Salidas:

Esta función contiene una pi caracteres Unicode (π)

Esta función contiene una pi caracteres Unicode (\ u03a0)

Esta función contiene una pi caracteres Unicode (π)

+1

DecodeEncodedNonAsciiCharacters lanzará FormatException para cadenas como "\\ user" – vovafeldman

+3

\ user no debe coincidir porque no hay 4 caracteres después de la u, pero entiendo su punto. Simplemente cambie la coincidencia de caracteres regex a [a-fA-F0-9]. Todavía coincidirá con cosas que no se pretende que coincidan, pero parece que todavía coincide con la intención de la pregunta original. –

+2

Se ve bien y limpio. Aún así, estoy sorprendido de que no haya una clase System .Net que haga esto. – saarp

0

Es necesario utilizar el método Convert() en la clase Encoding:

  • crear un objeto Encoding que representa la codificación ASCII
  • Crear un objeto Encoding que representa la codificación Unicode
  • llamada Encoding.Convert() con la codificación de origen , la codificación de destino y la cadena codificada

Hay un ejemplo here:

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); 
     } 
    } 
} 
+3

yo probamos este hecho. El problema es que convierte el carácter unicode π (\ u03a0) en "?". Necesito que lo convierta en "\ u03a0". – Ali

10
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); 
} 
+2

Cualquier cosa que no implique expresiones regulares tiene mi voto. –

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

Todos los caracteres no ASCII se convierten a su representación de Unicode Code Point y anexada a la cadena final.

0

Para almacenar puntos de código Unicode reales, primero debe decodificar las unidades de código UTF-16 de String a las unidades de códigos UTF-32 (que actualmente son las mismas que los puntos de código Unicode). Use System.Text.Encoding.UTF32.GetBytes() para eso y luego escriba los bytes resultantes en el StringBuilder según sea necesario, es decir.

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

Un pequeño parche para @ Adam respuesta de Sills que resuelve FormatException en los casos en que la cadena de entrada como "c: \ u00ab \ otherdirectory \" más RegexOptions.Compiled hace que el Regex compilación mucho más rápido:

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, @"\\"); 
    } 
3

Como de una sola línea:

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

Para Unescape Usted puede simplemente utilizar estas funciones:

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

System.Uri.UnescapeDataString(string) 

sugiere emplear este método (Funciona mejor con UTF-8):

UnescapeDataString(string) 
1

Aquí es mi implementación actual:

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

Esto pasa una prueba:

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

con el valor codificado: "\ud840\udc00"

Esta aplicación hace uso de un (enlace de la fuente de referencia) StringBuilderCache

Cuestiones relacionadas