私はこれをいじって楽しい時間を過ごしました。お役に立てれば。
Javascript では文字列への直接のバイト アクセスが許可されていないため、開始位置を見つける唯一の方法は順方向スキャンです。
更新 #3 char コードのシフトが実際に機能するとは思わない。正解が 3 のときに 2 バイトを読み取っている... なぜかこれをいつも忘れてしまいます。コードポイントは UTF8 と UTF16 で同じですが、エンコーディングに使用されるバイト数はエンコーディングによって異なります!!! したがって、これは正しい方法ではありません。
これは正しくありません。実際には、JavaScript には UTF-8 文字列はありません。ECMAScript 262 仕様によると、入力エンコーディングに関係なく、すべての文字列は内部的に UTF-16 ("[sequence of] 16-bit unsigned integers") として保存する必要があります。
これを考慮すると、8 ビット シフトは正しい (ただし不要)。
文字が 3 バイト シーケンスとして格納されているという仮定は間違っています。
実際、 JS (ECMA-262) 文字列内のすべての文字は 16 ビット (2 バイト) の長さです。
これは、以下のコードに示すように、マルチバイト文字を手動で utf-8 に変換することで回避できます。
私のコード例で説明されている詳細を参照してください。
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: "好吗"