2010-01-15 24 views
9

¿Es posible simplificar este código en una forma más limpia/más rápida?¿Podemos simplificar este código de codificación de cadena

StringBuilder builder = new StringBuilder(); 
var encoding = Encoding.GetEncoding(936); 

// convert the text into a byte array 
byte[] source = Encoding.Unicode.GetBytes(text); 

// convert that byte array to the new codepage. 
byte[] converted = Encoding.Convert(Encoding.Unicode, encoding, source); 

// take multi-byte characters and encode them as separate ascii characters 
foreach (byte b in converted) 
    builder.Append((char)b); 

// return the result 
string result = builder.ToString(); 

En pocas palabras, toma una cadena con caracteres chinos como 鄆 y los convierte a ài.

Por ejemplo, ese carácter chino en decimal es 37126 o 0x9106 en hexadecimal.

Ver http://unicodelookup.com/#0x9106/1

Modificada a una matriz de bytes, obtenemos [145, 6] (145 * 256 + 6 = 37126). Cuando se codifica en CodePage 936 (chino simplificado), obtenemos [224, 105]. Si dividimos este conjunto de bytes en caracteres individuales, tenemos 224 = e0 = à y 105 = 69 = i en Unicode.

Ver http://unicodelookup.com/#0x00e0/1 y http://unicodelookup.com/#0x0069/1

Por lo tanto, estamos haciendo una conversión de codificación y asegurar que todos los personajes de nuestra cadena Unicode de salida pueden ser representados usando un máximo de dos bytes.

Actualización: Necesito esta representación final porque este es el formato que acepta mi impresora de recibos. Me tomó para siempre para resolverlo! :) Como no soy un experto en codificación, estoy buscando un código más simple o más rápido, pero el resultado debe seguir siendo el mismo.

actualización (versión limpia):

return Encoding.GetEncoding("ISO-8859-1").GetString(Encoding.GetEncoding(936).GetBytes(text)); 
+0

Supongo que su impresora de recibos no acepta cadenas .NET, entonces, ¿qué está enviando exactamente a la impresora de recibos? Blobs de texto? Si es así, todo el texto se codifica a través del cable, por lo que hay una buena posibilidad de que haya alguna codificación oculta pasando más adelante en el proceso; podría ser más fácil encontrar la "mejor" solución si estuviera claro * cómo * te estás comunicando con la impresora. –

+0

Estoy usando POS para .NET ... acepta cadenas y funcionó bien siempre y cuando permanezca en CodePage 1252 ... pero cambiar a 936 causó problemas, lo que se debe a la forma en que esta impresora específica reconoce estos caracteres. –

+0

A menudo es más rápido asignar una matriz de caracteres, usar un bucle for para asignarlo y luego usar el constructor de cadena para convertirlo en una cadena, en lugar de anexar una cadena carácter por carácter. – Brian

Respuesta

9

Bueno, para empezar, no necesita convertir la representación de cadena "incorporada" a una matriz de bytes antes de llamar al Encoding.Convert.

Se podía hacer:

byte[] converted = Encoding.GetEncoding(936).GetBytes(text); 

A continuación, reconstruir una cadena a partir de ese conjunto de bytes mediante el cual los valores CHAR se asignan directamente a los bytes, que podría hacer ...

static string MangleTextForReceiptPrinter(string text) { 
    return new string(
     Encoding.GetEncoding(936) 
      .GetBytes(text) 
      .Select(b => (char) b) 
      .ToArray()); 
} 

I wouldn no te preocupes demasiado por la eficiencia; ¿Cuántos MB/seg va a imprimir en una impresora de recibos de todos modos?

Joe señaló que hay una codificación que asigna directamente los valores de byte 0-255 para codificar puntos, y es la edad de edad, Latin1, lo que nos permite acortar la función para ...

return Encoding.GetEncoding("Latin1").GetString(
      Encoding.GetEncoding(936).GetBytes(text) 
     ); 

Por el De esta forma, si se trata de una API con errores de Windows (que, por lo que se ve), usted podría tratarse de con codepage 1252 (lo cual es casi idéntico). Puede intentar reflector para ver qué está haciendo con su System.String antes de enviarlo a través del cable.

+0

¡Vea mi actualización sobre por qué necesito ese formato final! –

+0

¡Tu código es lo suficientemente bueno para mí! Me preguntaba si había una función de fragmentación completa de la que no era consciente de que sería más eficiente que mi ciclo. :) –

6

Casi cualquier cosa sería más limpio que esto - que realmente está abusando de texto aquí, la OMI. Está intentando representar datos binarios opacos de manera efectiva (el texto codificado) como datos de texto ... por lo que posiblemente obtendrá cosas como caracteres de campana, escapes, etc.

La forma normal de codificar datos binarios opacos en el texto es base 64, por lo que podría utilizar:

return Convert.ToBase64String(Encoding.GetEncoding(936).GetBytes(text)); 

El texto resultante será totalmente ASCII, que es mucho menos probable que cause que problemas.

EDITAR: Si necesita esa salida, le recomiendo encarecidamente que la represente como una matriz de bytes en lugar de como una cadena ... páselo como una matriz de bytes a partir de ese momento, para que no tenga tentaciones para realizar operaciones de cadena en él.

+0

+1. Sospecho que el enfoque del OP no siempre será reversible. Lo que significa que podrá codificar algunos datos pero no decodificarlos correctamente. – LBushkin

+0

La codificación final es requerida por una impresora de recibos a la que envío datos. –

3

¿Su impresora de recibos tiene una API que acepta una matriz de bytes en lugar de una cadena? Si es así, puede simplificar el código para una sola conversión, desde una cadena Unicode a una matriz de bytes utilizando la codificación utilizada por la impresora de recibos.

Además, si quiere convertir una matriz de bytes a una cadena cuyos valores de caracteres corresponden 1-1 a los valores de los bytes, puede usar la página de códigos 28591 aka Latin1 aka ISO-8859-1.

es decir, la siguiente

foreach (byte b in converted) 
    builder.Append((char)b); 

string result = builder.ToString(); 

puede ser sustituido por:

// All three of the following are equivalent 
// string result = Encoding.GetEncoding(28591).GetString(converted); 
// string result = Encoding.GetEncoding("ISO-8859-1").GetString(converted); 
string result = Encoding.GetEncoding("Latin1").GetString(converted); 

Latin1 es una codificación útil cuando se desea para codificar datos binarios en una cadena, por ejemplo, para enviar a través de un puerto serie.

+0

Desafortunadamente no. ¡Si lo hubiera hecho, no habría pasado tanto tiempo tratando de entender su esquema de codificación críptica! –

+0

Probablemente internamente está convirtiendo la cadena Unicode nuevamente en una matriz de bytes para su transmisión a la impresora, quizás usando una codificación como Latin1. – Joe

+0

¡Agradable! No sabía que la conversión a Latin-1 reemplazaría mi ciclo. –

Cuestiones relacionadas