Como encontrar se uma string particular tem caracteres unicode (esp. Caracteres de dois bytes)

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Para ser mais preciso, eu preciso saber se (e, se possível, como) eu posso descobrir se uma determinada string tem caracteres de dois bytes ou não. Basicamente, eu preciso abrir uma janela pop-up para exibir um determinado texto que pode conter caracteres de bytes duplos, como chinês ou japonês. Neste caso, é preciso ajustar o tamanho da janela do que seria para Inglês ou ASCII. Alguém tem uma pista?

Foi útil?

Solução

JavaScript contém texto internamente como UCS-2, que pode codificar uma bastante extensa subconjunto de Unicode.

Mas isso não é realmente pertinente para sua pergunta. Uma solução poderia ser a de percorrer a string e examinar os códigos de caracteres em cada posição:

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

Isto pode não ser tão rápido quanto você gostaria.

Outras dicas

Eu costumava resposta mikesamuel em um presente. No entanto notei talvez porque desta forma que deve haver apenas uma barra de escape antes do u, por exemplo, \u e não \\u para fazer este trabalho corretamente.

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

funciona para mim:)

Eu comparou as duas funções nas primeiras respostas e pensei que eu iria partilhar os resultados. Aqui está o código de teste que eu usei:

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

Ao executar este eu tenho:

isDoubleByte => 2421
containsNonLatinCodepoints => 868

Assim, para esta corda particular, a solução regex é de cerca de 3 vezes mais rápido.

No entanto, note que para uma cadeia onde o primeiro caractere é Unicode, isDoubleByte() retorna imediatamente e por isso é muito mais rápido do que a regex (que ainda tem a sobrecarga da expressão regular).

Por exemplo, para o 中国 corda, eu tenho estes resultados:

isDoubleByte => 51
containsNonLatinCodepoints => 288

Para obter o melhor dos dois mundo, é provavelmente melhor para combinar:

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

Nesse caso, se o primeiro caractere é chinês (o que é provável se o texto inteiro é chinês), a função será rápido e voltar imediatamente. Se não, ele vai executar o regex, que ainda é mais rápido do que verificar cada personagem individualmente.

Na verdade, todos os personagens são Unicode, pelo menos do ponto de vista do motor Javascript.

Infelizmente, a mera presença de caracteres em um intervalo Unicode especial não será suficiente para determinar que você precisa de mais espaço. Há uma série de personagens que ocupam aproximadamente a mesma quantidade de espaço como outros personagens que têm codepoints Unicode, bem acima do intervalo ASCII. aspas tipográficas, caracteres com sinais diacríticos, certos símbolos de pontuação, e vários símbolos de moeda estão fora da faixa de baixa ASCII e são alocados em lugares muito diferentes no plano multilíngüe básico Unicode.

Geralmente, projetos que eu trabalhei eleito para fornecer espaço extra para todos os idiomas, ou, por vezes, usar javascript para determinar se uma janela com css auto-rolagem atributos realmente tem conteúdo com uma altura que iria desencadear uma barra de rolagem ou não.

Se detectar a presença de, ou contar de, caracteres CJK será suficiente para determinar que você precisa de um pouco de espaço extra, você poderia construir um regex usando as seguintes especificações: [\ U3300- \ u9fff \ uf900- \ ufaff], e usar isso para extrair uma contagem do número de caracteres que jogo. (Isto é um pouco excessivamente grossa, e perde todos os casos não-BMP, provavelmente exclui algumas outras faixas relevantes, e muito provavelmente inclui alguns caracteres irrelevantes, mas é um ponto de partida).

Mais uma vez, você só vai ser capaz de gerir uma heurística áspero sem algo ao longo das linhas de um motor de renderização de texto completo, porque o que você realmente quer é algo como MeasureString de GDI (ou qualquer outro motor de renderização de texto equivalente). Tem sido um tempo desde que eu fiz isso, mas acho que o HTML / DOM equivalente mais próximo está a definir uma largura em um div e solicitando a altura (cortar e colar reutilização, então desculpas se este contém erros):

o = document.getElementById("test");

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

Aqui é teste de benchmark: http://jsben.ch/NKjKd

Este é muito mais rápido:

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

que isso:

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

Por que não deixar o próprio redimensionamento janela com base na altura runtime / largura?

Executar algo como isto em seu pop-up:

window.resizeTo(document.body.clientWidth, document.body.clientHeight);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top