2012-06-26 9 views
9

Ho una stringa e inizio e lunghezza con cui estrarre una sottostringa. Entrambe le posizioni (inizio e lunghezza) sono basate sugli offset di byte nella stringa UTF8 originale.Estrazione della sottostringa in base alle posizioni utf-8 byte

Tuttavia, c'è un problema:

L'inizio e la durata sono in byte, quindi non posso usare "stringa". La stringa UTF8 contiene diversi caratteri multibyte. C'è un modo iperefficiente per farlo? (Non ho bisogno di decodificare i byte ...)

Esempio: var orig = '? 你 好吗'

I s, e potrebbe essere 3,3 per estrarre il secondo carattere (好). Sto cercando

var result = orig.substringBytes(3,3); 

Help!

Aggiornamento n. 1 In C/C++ vorrei solo trasmettere a un array di byte, ma non sono sicuro se c'è un equivalente in javascript. A proposito, sì, potremmo analizzarlo in un array di byte e analizzarlo su una stringa, ma sembra che ci dovrebbe essere un modo rapido per tagliarlo nel posto giusto. Immagina che 'orig' sia 1000000 caratteri, s = 6 byte e l = 3 byte.

Aggiornamento # 2 Grazie a zerkms utile ri-direzione, ho finito con il seguente, che fa NON lavoro giusto - funziona giusto per multibyte ma incasinato per singolo byte.

function substrBytes(str, start, length) 
{ 
    var ch, startIx = 0, endIx = 0, re = ''; 
    for (var i = 0; 0 < str.length; i++) 
    { 
     startIx = endIx++; 

     ch = str.charCodeAt(i); 
     do { 
      ch = ch >> 8; // a better way may exist to measure ch len 
      endIx++; 
     } 
     while (ch); 

     if (endIx > start + length) 
     { 
      return re; 
     } 
     else if (startIx >= start) 
     { 
      re += str[i]; 
     } 
    } 
} 

Update # 3 non credo che spostando il codice char funziona davvero. Sto leggendo due byte quando la risposta corretta è tre ... in qualche modo lo dimentico sempre. Il punto di codice è lo stesso per UTF8 e UTF16, ma il numero di byte occupati dalla codifica dipende dalla codifica !!! Quindi questo non è il modo giusto per farlo.

+0

L'inizio e la lunghezza di 'substr' sono in carattere, non in byte. – nhahtdh

+0

http://stackoverflow.com/q/1240408/251311 – zerkms

+1

@zerkms - L'ho trovato anch'io, anche se penso che decodificare l'intera stringa in byte, prelevare la sottostringa e tornare indietro sarebbe davvero inefficiente. Cosa succede se ci sono 10000000 caratteri e voglio i byte 6-12? Sembra che la conversione dell'intera stringa sarebbe una pessima idea. – tofutim

risposta

7

ho avuto un momento di divertimento giocherellare con questo. Spero che questo ti aiuti.

Poiché Javascript non consente l'accesso diretto ai byte su una stringa, l'unico modo per trovare la posizione iniziale è una scansione in avanti.


Update # 3 non credo che spostando il codice char funziona davvero. Sto leggendo due byte quando la risposta corretta è tre ... in qualche modo lo dimentico sempre. Il punto di codice è lo stesso per UTF8 e UTF16, ma il numero di byte occupati dalla codifica dipende dalla codifica !!! Quindi questo non è il modo giusto per farlo.

Questo non è corretto - In realtà non esiste una stringa UTF-8 in javascript. Secondo la specifica ECMAScript 262, tutte le stringhe, indipendentemente dalla codifica in ingresso, devono essere memorizzate internamente come UTF-16 ("[sequenza di] interi senza segno a 16 bit").

Considerato ciò, lo spostamento di 8 bit è corretto (ma non necessario).

errato è presupposto che il personaggio è memorizzata come una sequenza di 3 byte ...
Infatti, tutti caratteri di una stringa JS (ECMA-262) sono 16 bit (2 byte) lunghi.

Questo può essere risolto convertendo manualmente i caratteri multibyte in utf-8, come mostrato nel codice seguente.


Vedi i dettagli spiegati nel mio codice di esempio:

function encode_utf8(s) 
{ 
    return unescape(encodeURIComponent(s)); 
} 

function substr_utf8_bytes(str, startInBytes, lengthInBytes) { 

    /* this function scans a multibyte string and returns a substring. 
    * arguments are start position and length, both defined in bytes. 
    * 
    * this is tricky, because javascript only allows character level 
    * and not byte level access on strings. Also, all strings are stored 
    * in utf-16 internally - so we need to convert characters to utf-8 
    * to detect their length in utf-8 encoding. 
    * 
    * the startInBytes and lengthInBytes parameters are based on byte 
    * positions in a utf-8 encoded string. 
    * in utf-8, for example: 
    *  "a" is 1 byte, 
      "ü" is 2 byte, 
     and "你" is 3 byte. 
    * 
    * NOTE: 
    * according to ECMAScript 262 all strings are stored as a sequence 
    * of 16-bit characters. so we need a encode_utf8() function to safely 
    * detect the length our character would have in a utf8 representation. 
    * 
    * http://www.ecma-international.org/publications/files/ecma-st/ECMA-262.pdf 
    * see "4.3.16 String Value": 
    * > Although each value usually represents a single 16-bit unit of 
    * > UTF-16 text, the language does not place any restrictions or 
    * > requirements on the values except that they be 16-bit unsigned 
    * > integers. 
    */ 

    var resultStr = ''; 
    var startInChars = 0; 

    // scan string forward to find index of first character 
    // (convert start position in byte to start position in characters) 

    for (bytePos = 0; bytePos < startInBytes; startInChars++) { 

     // get numeric code of character (is >128 for multibyte character) 
     // and increase "bytePos" for each byte of the character sequence 

     ch = str.charCodeAt(startInChars); 
     bytePos += (ch < 128) ? 1 : encode_utf8(str[startInChars]).length; 
    } 

    // now that we have the position of the starting character, 
    // we can built the resulting substring 

    // as we don't know the end position in chars yet, we start with a mix of 
    // chars and bytes. we decrease "end" by the byte count of each selected 
    // character to end up in the right position 
    end = startInChars + lengthInBytes - 1; 

    for (n = startInChars; startInChars <= end; n++) { 
     // get numeric code of character (is >128 for multibyte character) 
     // and decrease "end" for each byte of the character sequence 
     ch = str.charCodeAt(n); 
     end -= (ch < 128) ? 1 : encode_utf8(str[n]).length; 

     resultStr += str[n]; 
    } 

    return resultStr; 
} 

var orig = 'abc你好吗?'; 

alert('res: ' + substr_utf8_bytes(orig, 0, 2)); // alerts: "ab" 
alert('res: ' + substr_utf8_bytes(orig, 2, 1)); // alerts: "c" 
alert('res: ' + substr_utf8_bytes(orig, 3, 3)); // alerts: "你" 
alert('res: ' + substr_utf8_bytes(orig, 6, 6)); // alerts: "好吗" 
+0

aggiornato per rendere questa funzione compatibile con l'input UTF-8. (se inizialmente la stringa era utf-8 e le posizioni dei byte erano anche quelle di una stringa utf-8) – Kaii

0

System.ArraySegment è utile, ma è necessario il costruttore con input e offset e indexer di array.

+0

È in javascript? O solo una libreria C#? – tofutim

1
function substrBytes(str, start, length) 
{ 
    var buf = new Buffer(str); 
    return buf.slice(start, start+length).toString(); 
} 

AYB

+0

ho provato questo, ma non ho alcun oggetto Buffer(). quale quadro hai usato? – Kaii

+0

Si trova in node.js – tofutim

+0

Questo non funziona per me in Node.js. Restituisce un gruppo di caratteri punto interrogativo. Il substr normale funziona bene. – Gavin

5

s' @Kaii risposta è quasi corretta, ma c'è un bug in esso. Non riesce a gestire i caratteri Unicode delle quali sono da 128 a 255. Qui è la versione rivista (basta cambiare 256-128):

function encode_utf8(s) 
{ 
    return unescape(encodeURIComponent(s)); 
} 

function substr_utf8_bytes(str, startInBytes, lengthInBytes) { 

    /* this function scans a multibyte string and returns a substring. 
    * arguments are start position and length, both defined in bytes. 
    * 
    * this is tricky, because javascript only allows character level 
    * and not byte level access on strings. Also, all strings are stored 
    * in utf-16 internally - so we need to convert characters to utf-8 
    * to detect their length in utf-8 encoding. 
    * 
    * the startInBytes and lengthInBytes parameters are based on byte 
    * positions in a utf-8 encoded string. 
    * in utf-8, for example: 
    *  "a" is 1 byte, 
      "ü" is 2 byte, 
     and "你" is 3 byte. 
    * 
    * NOTE: 
    * according to ECMAScript 262 all strings are stored as a sequence 
    * of 16-bit characters. so we need a encode_utf8() function to safely 
    * detect the length our character would have in a utf8 representation. 
    * 
    * http://www.ecma-international.org/publications/files/ecma-st/ECMA-262.pdf 
    * see "4.3.16 String Value": 
    * > Although each value usually represents a single 16-bit unit of 
    * > UTF-16 text, the language does not place any restrictions or 
    * > requirements on the values except that they be 16-bit unsigned 
    * > integers. 
    */ 

    var resultStr = ''; 
    var startInChars = 0; 

    // scan string forward to find index of first character 
    // (convert start position in byte to start position in characters) 

    for (bytePos = 0; bytePos < startInBytes; startInChars++) { 

     // get numeric code of character (is >= 128 for multibyte character) 
     // and increase "bytePos" for each byte of the character sequence 

     ch = str.charCodeAt(startInChars); 
     bytePos += (ch < 128) ? 1 : encode_utf8(str[startInChars]).length; 
    } 

    // now that we have the position of the starting character, 
    // we can built the resulting substring 

    // as we don't know the end position in chars yet, we start with a mix of 
    // chars and bytes. we decrease "end" by the byte count of each selected 
    // character to end up in the right position 
    end = startInChars + lengthInBytes - 1; 

    for (n = startInChars; startInChars <= end; n++) { 
     // get numeric code of character (is >= 128 for multibyte character) 
     // and decrease "end" for each byte of the character sequence 
     ch = str.charCodeAt(n); 
     end -= (ch < 128) ? 1 : encode_utf8(str[n]).length; 

     resultStr += str[n]; 
    } 

    return resultStr; 
} 

var orig = 'abc你好吗?©'; 

alert('res: ' + substr_utf8_bytes(orig, 0, 2)); // alerts: "ab" 
alert('res: ' + substr_utf8_bytes(orig, 2, 1)); // alerts: "c" 
alert('res: ' + substr_utf8_bytes(orig, 3, 3)); // alerts: "你" 
alert('res: ' + substr_utf8_bytes(orig, 6, 6)); // alerts: "好吗" 
alert('res: ' + substr_utf8_bytes(orig, 15, 2)); // alerts: "©" 

Tra l'altro, si tratta di un bug fix, e dovrebbe essere utile per quelli che hanno lo stesso problema. Perché i revisori hanno rifiutato il mio suggerimento di modifica a causa del cambiamento "troppo" o "troppo piccolo"? @Adam Eberlin@Kjuly@Jasonw

+0

ho preso questo in credito e modificato la mia risposta. grazie per i tuoi occhi acuti – Kaii

0

Per gli utenti di IE, i codici nella risposta sopra forniranno undefined. Perché, in IE, non è supportato str[n], in altre parole, non è possibile utilizzare stringa come matrice. La tua necessità di sostituire str[n] con str.charAt(n). Il codice dovrebbe essere;

function encode_utf8(s) { 
    return unescape(encodeURIComponent(s)); 
} 

function substr_utf8_bytes(str, startInBytes, lengthInBytes) { 

    var resultStr = ''; 
    var startInChars = 0; 

    for (bytePos = 0; bytePos < startInBytes; startInChars++) { 
     ch = str.charCodeAt(startInChars); 
     bytePos += (ch < 128) ? 1 : encode_utf8(str.charAt(startInChars)).length; 
    } 

    end = startInChars + lengthInBytes - 1; 

    for (n = startInChars; startInChars <= end; n++) { 
     ch = str.charCodeAt(n); 
     end -= (ch < 128) ? 1 : encode_utf8(str.charAt(n)).length; 

     resultStr += str.charAt(n); 
    } 

    return resultStr; 
} 
0

Forse utilizzare questo per contare il byte e l'esempio. Conta 你 carattere è 2 byte, invece 3 byte seguono la funzione di @Kaii:

jQuery.byteLength = function(target) { 
    try { 
     var i = 0; 
     var length = 0; 
     var count = 0; 
     var character = ''; 
     // 
     target = jQuery.castString(target); 
     length = target.length; 
     // 
     for (i = 0; i < length; i++) { 
      // 1 文字を切り出し Unicode に変換 
      character = target.charCodeAt(i); 
      // 
      // Unicode の半角 : 0x0 - 0x80, 0xf8f0, 0xff61 - 0xff9f, 0xf8f1 - 
      // 0xf8f3 
      if ((character >= 0x0 && character < 0x81) 
        || (character == 0xf8f0) 
        || (character > 0xff60 && character < 0xffa0) 
        || (character > 0xf8f0 && character < 0xf8f4)) { 
       // 1 バイト文字 
       count += 1; 
      } else { 
       // 2 バイト文字 
       count += 2; 
      } 
     } 
     // 
     return (count); 
    } catch (e) { 
     jQuery.showErrorDetail(e, 'byteLength'); 
     return (0); 
    } 
}; 

for (var j = 1, len = value.length; j <= len; j++) { 
    var slice = value.slice(0, j); 
    var slength = $.byteLength(slice); 
    if (slength == 106) { 
     $(this).val(slice); 
     break; 
    } 
} 
Problemi correlati