Cómo encontrar si una cadena particular tiene caracteres unicode (especialmente caracteres de doble byte)

StackOverflow https://stackoverflow.com/questions/147824

  •  02-07-2019
  •  | 
  •  

Pregunta

Para ser más precisos, necesito saber si (y si es posible, cómo) puedo encontrar si una cadena dada tiene caracteres de doble byte o no. Básicamente, necesito abrir una ventana emergente para mostrar un texto dado que puede contener caracteres de doble byte, como chino o japonés. En este caso, debemos ajustar el tamaño de la ventana de lo que sería para el inglés o ASCII. ¿Alguien tiene una pista?

¿Fue útil?

Solución

JavaScript mantiene el texto internamente como UCS-2, que puede codificar un subconjunto bastante extenso de Unicode.

Pero eso no es realmente pertinente a tu pregunta. Una solución podría ser recorrer la cadena y examinar los códigos de caracteres en cada posición:

function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

Esto podría no ser tan rápido como te gustaría.

Otros consejos

Utilicé la respuesta de mikesamuel en este caso. Sin embargo, debido a este formulario, me di cuenta de que solo debería haber una barra de escape antes de u , por ejemplo. \ u y no \\ u para que esto funcione correctamente.

function containsNonLatinCodepoints(s) {
    return /[^\u0000-\u00ff]/.test(s);
}

Funciona para mí :)

He comparado las dos funciones en las respuestas principales y pensé en compartir los resultados. Aquí está el código de prueba que utilicé:

const text1 = `The Chinese Wikipedia was established along with 12 other Wikipedias in May 2001. 中文維基百科的副標題是「海納百川,有容乃大」,這是中国的清朝政治家林则徐(1785年-1850年)於1839年為`;

const regex = /[^\u0000-\u00ff]/; // Small performance gain from pre-compiling the regex
function containsNonLatinCodepoints(s) {
    return regex.test(s);
}

function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

function benchmark(fn, str) {
    let startTime = new Date();
    for (let i = 0; i < 10000000; i++) {
        fn(str);
    }   
    let endTime = new Date();

    return endTime.getTime() - startTime.getTime();
}

console.info('isDoubleByte => ' + benchmark(isDoubleByte, text1));
console.info('containsNonLatinCodepoints => ' + benchmark(containsNonLatinCodepoints, text1));

Al ejecutar esto obtuve:

isDoubleByte => 2421
containsNonLatinCodepoints => 868

Entonces, para esta cadena en particular, la solución regex es aproximadamente 3 veces más rápida.

Sin embargo, tenga en cuenta que para una cadena donde el primer carácter es unicode, isDoubleByte () regresa de inmediato y, por lo tanto, es mucho más rápido que la expresión regular (que todavía tiene la sobrecarga de la expresión regular).

Por ejemplo, para la cadena , obtuve estos resultados:

isDoubleByte => 51
containsNonLatinCodepoints => 288

Para obtener lo mejor de ambos mundos, probablemente sea mejor combinar ambos:

var regex = /[^\u0000-\u00ff]/; // Small performance gain from pre-compiling the regex
function containsDoubleByte(str) {
    if (!str.length) return false;
    if (str.charCodeAt(0) > 255) return true;
    return regex.test(str);
}

En ese caso, si el primer carácter es chino (lo que es probable si todo el texto es chino), la función será rápida y volverá de inmediato. De lo contrario, ejecutará la expresión regular, que es aún más rápida que revisar cada carácter individualmente.

En realidad, todos los caracteres son Unicode, al menos desde la perspectiva del motor Javascript.

Desafortunadamente, la mera presencia de caracteres en un rango Unicode en particular no será suficiente para determinar que necesita más espacio. Hay una serie de caracteres que ocupan aproximadamente la misma cantidad de espacio que otros caracteres que tienen puntos de código Unicode muy por encima del rango ASCII. Las comillas tipográficas, los caracteres con signos diacríticos, ciertos símbolos de puntuación y varios símbolos de moneda están fuera del rango ASCII bajo y se asignan en lugares bastante dispares en el plano multilingüe básico de Unicode.

En general, los proyectos en los que he trabajado eligen proporcionar espacio adicional para todos los idiomas, o a veces usan JavaScript para determinar si una ventana con atributos css de barra de desplazamiento automático realmente tiene contenido con una altura que activaría una barra de desplazamiento o no.

Si la detección de la presencia o la cantidad de caracteres CJK será adecuada para determinar que necesita un poco de espacio adicional, puede construir una expresión regular utilizando los siguientes rangos: [\ u3300- \ u9fff \ uf900- \ ufaff], y úselo para extraer una cuenta de la cantidad de caracteres que coinciden. (Esto es un poco excesivamente grosero, y pasa por alto todos los casos que no son BMP, probablemente excluye algunos otros rangos relevantes, y probablemente incluye algunos caracteres irrelevantes, pero es un punto de partida).

Nuevamente, solo podrá administrar una heurística aproximada sin algo similar a un motor de representación de texto completo, porque lo que realmente quiere es algo como MeasureString de GDI (o cualquier otro motor de representación de texto equivalente). Ha pasado un tiempo desde que lo hice, pero creo que el equivalente HTML / DOM más cercano es establecer un ancho en un div y solicitar la altura (reutilizar cortar y pegar, así que disculpas si esto contiene errores):

o = document.getElementById("test");

document.defaultView.getComputedStyle(o,"").getPropertyValue("height"))

Aquí está la prueba de referencia: http://jsben.ch/NKjKd

Esto es mucho más rápido:

function containsNonLatinCodepoints(s) {
    return /[^\u0000-\u00ff]/.test(s);
}

que esto:

function isDoubleByte(str) {
    for (var i = 0, n = str.length; i < n; i++) {
        if (str.charCodeAt( i ) > 255) { return true; }
    }
    return false;
}

¿Por qué no dejar que la ventana cambie de tamaño en función de la altura / anchura del tiempo de ejecución?

Ejecuta algo como esto en tu ventana emergente:

window.resizeTo(document.body.clientWidth, document.body.clientHeight);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top