cvs: pecl /intl php_intl.c /intl/doc grapheme_api.php /intl/grapheme grapheme.h grapheme_string.c /intl/tests grapheme.phpt
| From: | Ed Batutis | Date: | Wed, 30 Apr 2008 20:18:56 +0000 |
| Subject: | cvs: pecl /intl php_intl.c /intl/doc grapheme_api.php /intl/grapheme grapheme.h grapheme_string.c /intl/tests grapheme.phpt | ||
| Groups: | php.pecl.cvs | ||
| Request: | Send a blank email to pecl-cvs+get-10659@lists.php.net to get a copy of this message | ||
ebatutis Wed Apr 30 20:18:56 2008 UTC
Modified files:
/pecl/intl php_intl.c
/pecl/intl/doc grapheme_api.php
/pecl/intl/grapheme grapheme.h grapheme_string.c
/pecl/intl/tests grapheme.phpt
Log:
code, doc, tests for new function graphme_extractc
http://cvs.php.net/viewvc.cgi/pecl/intl/php_intl.c?r1=1.15&r2=1.16&diff_format=u Index: pecl/intl/php_intl.c diff -u pecl/intl/php_intl.c:1.15 pecl/intl/php_intl.c:1.16 --- pecl/intl/php_intl.c:1.15 Wed Apr 9 12:08:07 2008 +++ pecl/intl/php_intl.c Wed Apr 30 20:18:56 2008 @@ -283,7 +283,7 @@ PHP_FE( grapheme_stristr, grapheme_3_args ) PHP_FE( grapheme_extract, grapheme_3_args ) PHP_FE( grapheme_extractb, grapheme_3_args ) - + PHP_FE( grapheme_extractc, grapheme_3_args ) // common functions PHP_FE( intl_get_error_code, intl_0_args ) http://cvs.php.net/viewvc.cgi/pecl/intl/doc/grapheme_api.php?r1=1.1&r2=1.2&diff_format=u Index: pecl/intl/doc/grapheme_api.php diff -u pecl/intl/doc/grapheme_api.php:1.1 pecl/intl/doc/grapheme_api.php:1.2 --- pecl/intl/doc/grapheme_api.php:1.1 Fri Apr 4 16:58:13 2008 +++ pecl/intl/doc/grapheme_api.php Wed Apr 30 20:18:56 2008 @@ -112,15 +112,24 @@ /** - * Function to extract a sequence of default grapheme clusters from a text buffer, which must be encoded in UTF-8. + * Function to extract a sequence of default grapheme clusters from a UTF-8 text buffer limited by a byte count. * @param string $haystack string to search * @param int $bsize maximum number of bytes to return * @param [int] $start starting position in $haystack in bytes * @return string A string starting at offset $start containing no more than $bsize bytes and ending on a default grapheme cluster boundary. */ - public function grapheme_extractB($haystack, $bsize, $start = 0) {} + public function grapheme_extractb($haystack, $bsize, $start = 0) {} + /** + * Function to extract a sequence of default grapheme clusters from a UTF-8 text buffer limited by a character count. + * @param string $haystack string to search + * @param int $csize maximum number of characters to return + * @param [int] $start starting position in $haystack in bytes + * @return string A string starting at offset $start containing no more than $csize characters + and ending on a default grapheme cluster boundary. + */ + public function grapheme_extractc($haystack, $bsize, $start = 0) {} ?> http://cvs.php.net/viewvc.cgi/pecl/intl/grapheme/grapheme.h?r1=1.2&r2=1.3&diff_format=u Index: pecl/intl/grapheme/grapheme.h diff -u pecl/intl/grapheme/grapheme.h:1.2 pecl/intl/grapheme/grapheme.h:1.3 --- pecl/intl/grapheme/grapheme.h:1.2 Wed Apr 9 12:08:07 2008 +++ pecl/intl/grapheme/grapheme.h Wed Apr 30 20:18:56 2008 @@ -31,6 +31,7 @@ PHP_FUNCTION(grapheme_stristr); PHP_FUNCTION(grapheme_extract); PHP_FUNCTION(grapheme_extractb); +PHP_FUNCTION(grapheme_extractc); void grapheme_register_constants( INIT_FUNC_ARGS ); void grapheme_close_global_iterator( TSRMLS_D ); http://cvs.php.net/viewvc.cgi/pecl/intl/grapheme/grapheme_string.c?r1=1.4&r2=1.5&diff_format=u Index: pecl/intl/grapheme/grapheme_string.c diff -u pecl/intl/grapheme/grapheme_string.c:1.4 pecl/intl/grapheme/grapheme_string.c:1.5 --- pecl/intl/grapheme/grapheme_string.c:1.4 Sun Apr 27 19:27:57 2008 +++ pecl/intl/grapheme/grapheme_string.c Wed Apr 30 20:18:56 2008 @@ -867,6 +867,119 @@ /* }}} */ + +/* {{{ proto string grapheme_extractc(string str, bsize[, start]) + Function to extract a sequence of default grapheme clusters up to a character limit */ +PHP_FUNCTION(grapheme_extractc) +{ + char *str, *pstr; + UChar *ustr; + int str_len, ustr_len; + long csize; /* maximum number of characters to return */ + long start = 0; /* starting position in str in bytes */ + UErrorCode status; + unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE]; + UBreakIterator* bi = NULL; + int ret_pos, pos, prev_pos, prev_ret_pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", (char **)&str, &str_len, &csize, &start) == FAILURE) { + + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "grapheme_extractb: unable to parse input param", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if ( start < 0 || start > str_len ) { + + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extractc: start not contained in string", 1 TSRMLS_CC ); + + RETURN_FALSE; + } + + pstr = str + start; + + /* just in case pstr points in the middle of a character, move forward to the start of the next char */ + if ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) { + char *str_end = str + str_len; + + while ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) { + pstr++; + if ( pstr >= str_end ) { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "grapheme_extract: invalid input string", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + } + } + + str_len -= (pstr - str); + + /* convert the string to UTF-16. */ + ustr = NULL; + ustr_len = 0; + status = U_ZERO_ERROR; + intl_convert_utf8_to_utf16(&ustr, &ustr_len, pstr, str_len, &status ); + + if ( U_FAILURE( status ) ) { + /* Set global error code. */ + intl_error_set_code( NULL, status TSRMLS_CC ); + + /* Set error messages. */ + intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC ); + + if ( NULL != ustr ) + efree( ustr ); + + RETURN_FALSE; + } + + bi = NULL; + status = U_ZERO_ERROR; + bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC ); + + ubrk_setText(bi, ustr, ustr_len, &status); + + pos = 0; + prev_pos = 0; + ret_pos = 0; + prev_ret_pos = 0; + + while ( 1 ) { + pos = ubrk_next(bi); + + if ( UBRK_DONE == pos ) { + break; + } + + /* if we are beyond our limit, then the loop is done */ + if ( pos > csize ) { + break; + } + + /* update our pointer in the original UTF-8 buffer by as many characters + as ubrk_next iterated over */ + + prev_ret_pos = ret_pos; + U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos); + + if ( prev_ret_pos == ret_pos ) { + /* something wrong - malformed utf8? */ + break; + } + + prev_pos = pos; + } + + efree(ustr); + ubrk_close(bi); + + RETURN_STRINGL(pstr, ret_pos, 1); +} + +/* }}} */ + /* * Local variables: * tab-width: 4 http://cvs.php.net/viewvc.cgi/pecl/intl/tests/grapheme.phpt?r1=1.1&r2=1.2&diff_format=u Index: pecl/intl/tests/grapheme.phpt diff -u pecl/intl/tests/grapheme.phpt:1.1 pecl/intl/tests/grapheme.phpt:1.2 --- pecl/intl/tests/grapheme.phpt:1.1 Fri Apr 4 16:58:13 2008 +++ pecl/intl/tests/grapheme.phpt Wed Apr 30 20:18:56 2008 @@ -589,7 +589,7 @@ //===================================================================================== - $res_str .= "\n" . 'function grapheme_extractB($haystack, $size, $start = 0)' . "\n\n"; + $res_str .= "\n" . 'function grapheme_extractb($haystack, $size, $start = 0)' . "\n\n"; $tests = array( array( "abc", 3, "abc" ), @@ -627,13 +627,13 @@ foreach( $tests as $test ) { $arg0 = urlencode($test[0]); - $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractB"; + $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractb"; if ( 3 == count( $test ) ) { - $result = grapheme_extractB($test[0], $test[1]); + $result = grapheme_extractb($test[0], $test[1]); } else { $res_str .= " starting at byte position $test[2]"; - $result = grapheme_extractB($test[0], $test[1], $test[2]); + $result = grapheme_extractb($test[0], $test[1], $test[2]); } $res_str .= " = "; if ( $result === false ) { @@ -645,6 +645,68 @@ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n"; } + + //===================================================================================== + $res_str .= "\n" . 'function grapheme_extractc($haystack, $size, $start = 0)' . "\n\n"; + + $tests = array( + array( "abc", 3, "abc" ), + array( "abc", 2, "ab" ), + array( "abc", 1, "a" ), + array( "abc", 0, "" ), + array( "abc" . $char_o_diaeresis_nfd, 0, "" ), + array( "abc" . $char_o_diaeresis_nfd, 1, "a" ), + array( "abc" . $char_o_diaeresis_nfd, 2, "ab" ), + array( "abc" . $char_o_diaeresis_nfd, 3, "abc" ), + array( "abc" . $char_o_diaeresis_nfd, 4, "abc" ), + array( "abc" . $char_o_diaeresis_nfd, 5, "abc" . $char_o_diaeresis_nfd), + array( "abc" . $char_o_diaeresis_nfd, 6, "abc" . $char_o_diaeresis_nfd), + array( $char_o_diaeresis_nfd . "abc", 0, "" ), + array( $char_o_diaeresis_nfd . "abc", 1, "" ), + array( $char_o_diaeresis_nfd . "abc", 2, $char_o_diaeresis_nfd ), + array( $char_o_diaeresis_nfd . "abc", 3, $char_o_diaeresis_nfd . "a" ), + array( $char_o_diaeresis_nfd . "abc", 4, $char_o_diaeresis_nfd . "ab" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 5, $char_o_diaeresis_nfd . "abc" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 6, $char_o_diaeresis_nfd . "abc" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 7, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ), + + array( "abc", 3, 0, "abc" ), + array( "abc", 2, 1, "bc" ), + array( "abc", 1, 2, "c" ), + array( "abc", 0, 3, "" ), + array( "abc", 1, 3, "" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 0, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 1, $char_diaeresis . "abc" . $char_a_ring_nfd . "xy" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 2, "abc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 3, "abc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 4, "bc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 5, "c" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 6, $char_a_ring_nfd . "xyz" ), + + ); + + foreach( $tests as $test ) { + $arg0 = urlencode($test[0]); + $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractc"; + if ( 3 == count( $test ) ) { + $result = grapheme_extractc($test[0], $test[1]); + } + else { + $res_str .= " starting at byte position $test[2]"; + $result = grapheme_extractc($test[0], $test[1], $test[2]); + } + $res_str .= " = "; + if ( $result === false ) { + $res_str .= 'false'; + } + else { + $res_str .= urlencode($result); + } + $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n"; + } + + //===================================================================================== return $res_str; @@ -998,34 +1060,69 @@ extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 10 = %CC%88 == %CC%88 extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 11 = false == false -function grapheme_extractB($haystack, $size, $start = 0) +function grapheme_extractb($haystack, $size, $start = 0) -extract from "abc" "3" graphemes - grapheme_extractB = abc == abc -extract from "abc" "2" graphemes - grapheme_extractB = ab == ab -extract from "abc" "1" graphemes - grapheme_extractB = a == a -extract from "abc" "0" graphemes - grapheme_extractB = == -extract from "a%CC%8Abc" "5" graphemes - grapheme_extractB = a%CC%8Abc == a%CC%8Abc -extract from "a%CC%8Abc" "4" graphemes - grapheme_extractB = a%CC%8Ab == a%CC%8Ab -extract from "a%CC%8Abc" "1" graphemes - grapheme_extractB = == -extract from "a%CC%8Ao%CC%88o%CC%88" "9" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "10" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "11" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "6" graphemes - grapheme_extractB = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 -extract from "a%CC%8Ao%CC%88c" "3" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "4" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "5" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "6" graphemes - grapheme_extractB = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 -extract from "a%CC%8Ao%CC%88c" "7" graphemes - grapheme_extractB = a%CC%8Ao%CC%88c == a%CC%8Ao%CC%88c -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 0 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 2 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 3 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 4 = %CC%88 == %CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractB starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractB starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 8 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractB starting at byte position 10 = %CC%88 == %CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractB starting at byte position 11 = false == false +extract from "abc" "3" graphemes - grapheme_extractb = abc == abc +extract from "abc" "2" graphemes - grapheme_extractb = ab == ab +extract from "abc" "1" graphemes - grapheme_extractb = a == a +extract from "abc" "0" graphemes - grapheme_extractb = == +extract from "a%CC%8Abc" "5" graphemes - grapheme_extractb = a%CC%8Abc == a%CC%8Abc +extract from "a%CC%8Abc" "4" graphemes - grapheme_extractb = a%CC%8Ab == a%CC%8Ab +extract from "a%CC%8Abc" "1" graphemes - grapheme_extractb = == +extract from "a%CC%8Ao%CC%88o%CC%88" "9" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "10" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "11" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "6" graphemes - grapheme_extractb = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 +extract from "a%CC%8Ao%CC%88c" "3" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "4" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "5" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "6" graphemes - grapheme_extractb = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 +extract from "a%CC%8Ao%CC%88c" "7" graphemes - grapheme_extractb = a%CC%8Ao%CC%88c == a%CC%8Ao%CC%88c +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 0 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 2 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 3 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 4 = %CC%88 == %CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractb starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractb starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 8 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractb starting at byte position 10 = %CC%88 == %CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractb starting at byte position 11 = false == false + +function grapheme_extractc($haystack, $size, $start = 0) + +extract from "abc" "3" graphemes - grapheme_extractc = abc == abc +extract from "abc" "2" graphemes - grapheme_extractc = ab == ab +extract from "abc" "1" graphemes - grapheme_extractc = a == a +extract from "abc" "0" graphemes - grapheme_extractc = == +extract from "abco%CC%88" "0" graphemes - grapheme_extractc = == +extract from "abco%CC%88" "1" graphemes - grapheme_extractc = a == a +extract from "abco%CC%88" "2" graphemes - grapheme_extractc = ab == ab +extract from "abco%CC%88" "3" graphemes - grapheme_extractc = abc == abc +extract from "abco%CC%88" "4" graphemes - grapheme_extractc = abc == abc +extract from "abco%CC%88" "5" graphemes - grapheme_extractc = abco%CC%88 == abco%CC%88 +extract from "abco%CC%88" "6" graphemes - grapheme_extractc = abco%CC%88 == abco%CC%88 +extract from "o%CC%88abc" "0" graphemes - grapheme_extractc = == +extract from "o%CC%88abc" "1" graphemes - grapheme_extractc = == +extract from "o%CC%88abc" "2" graphemes - grapheme_extractc = o%CC%88 == o%CC%88 +extract from "o%CC%88abc" "3" graphemes - grapheme_extractc = o%CC%88a == o%CC%88a +extract from "o%CC%88abc" "4" graphemes - grapheme_extractc = o%CC%88ab == o%CC%88ab +extract from "o%CC%88abca%CC%8Axyz" "5" graphemes - grapheme_extractc = o%CC%88abc == o%CC%88abc +extract from "o%CC%88abca%CC%8Axyz" "6" graphemes - grapheme_extractc = o%CC%88abc == o%CC%88abc +extract from "o%CC%88abca%CC%8Axyz" "7" graphemes - grapheme_extractc = o%CC%88abca%CC%8A == o%CC%88abca%CC%8A +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax +extract from "abc" "3" graphemes - grapheme_extractc starting at byte position 0 = abc == abc +extract from "abc" "2" graphemes - grapheme_extractc starting at byte position 1 = bc == bc +extract from "abc" "1" graphemes - grapheme_extractc starting at byte position 2 = c == c +extract from "abc" "0" graphemes - grapheme_extractc starting at byte position 3 = == +extract from "abc" "1" graphemes - grapheme_extractc starting at byte position 3 = == +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 0 = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 1 = %CC%88abca%CC%8Axy == %CC%88abca%CC%8Axy +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 2 = abca%CC%8Axyz == abca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 3 = abca%CC%8Axyz == abca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 4 = bca%CC%8Axyz == bca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 5 = ca%CC%8Axyz == ca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 6 = a%CC%8Axyz == a%CC%8Axyz
http://cvs.php.net/viewvc.cgi/pecl/intl/php_intl.c?r1=1.15&r2=1.16&diff_format=u Index: pecl/intl/php_intl.c diff -u pecl/intl/php_intl.c:1.15 pecl/intl/php_intl.c:1.16 --- pecl/intl/php_intl.c:1.15 Wed Apr 9 12:08:07 2008 +++ pecl/intl/php_intl.c Wed Apr 30 20:18:56 2008 @@ -283,7 +283,7 @@ PHP_FE( grapheme_stristr, grapheme_3_args ) PHP_FE( grapheme_extract, grapheme_3_args ) PHP_FE( grapheme_extractb, grapheme_3_args ) - + PHP_FE( grapheme_extractc, grapheme_3_args ) // common functions PHP_FE( intl_get_error_code, intl_0_args ) http://cvs.php.net/viewvc.cgi/pecl/intl/doc/grapheme_api.php?r1=1.1&r2=1.2&diff_format=u Index: pecl/intl/doc/grapheme_api.php diff -u pecl/intl/doc/grapheme_api.php:1.1 pecl/intl/doc/grapheme_api.php:1.2 --- pecl/intl/doc/grapheme_api.php:1.1 Fri Apr 4 16:58:13 2008 +++ pecl/intl/doc/grapheme_api.php Wed Apr 30 20:18:56 2008 @@ -112,15 +112,24 @@ /** - * Function to extract a sequence of default grapheme clusters from a text buffer, which must be encoded in UTF-8. + * Function to extract a sequence of default grapheme clusters from a UTF-8 text buffer limited by a byte count. * @param string $haystack string to search * @param int $bsize maximum number of bytes to return * @param [int] $start starting position in $haystack in bytes * @return string A string starting at offset $start containing no more than $bsize bytes and ending on a default grapheme cluster boundary. */ - public function grapheme_extractB($haystack, $bsize, $start = 0) {} + public function grapheme_extractb($haystack, $bsize, $start = 0) {} + /** + * Function to extract a sequence of default grapheme clusters from a UTF-8 text buffer limited by a character count. + * @param string $haystack string to search + * @param int $csize maximum number of characters to return + * @param [int] $start starting position in $haystack in bytes + * @return string A string starting at offset $start containing no more than $csize characters + and ending on a default grapheme cluster boundary. + */ + public function grapheme_extractc($haystack, $bsize, $start = 0) {} ?> http://cvs.php.net/viewvc.cgi/pecl/intl/grapheme/grapheme.h?r1=1.2&r2=1.3&diff_format=u Index: pecl/intl/grapheme/grapheme.h diff -u pecl/intl/grapheme/grapheme.h:1.2 pecl/intl/grapheme/grapheme.h:1.3 --- pecl/intl/grapheme/grapheme.h:1.2 Wed Apr 9 12:08:07 2008 +++ pecl/intl/grapheme/grapheme.h Wed Apr 30 20:18:56 2008 @@ -31,6 +31,7 @@ PHP_FUNCTION(grapheme_stristr); PHP_FUNCTION(grapheme_extract); PHP_FUNCTION(grapheme_extractb); +PHP_FUNCTION(grapheme_extractc); void grapheme_register_constants( INIT_FUNC_ARGS ); void grapheme_close_global_iterator( TSRMLS_D ); http://cvs.php.net/viewvc.cgi/pecl/intl/grapheme/grapheme_string.c?r1=1.4&r2=1.5&diff_format=u Index: pecl/intl/grapheme/grapheme_string.c diff -u pecl/intl/grapheme/grapheme_string.c:1.4 pecl/intl/grapheme/grapheme_string.c:1.5 --- pecl/intl/grapheme/grapheme_string.c:1.4 Sun Apr 27 19:27:57 2008 +++ pecl/intl/grapheme/grapheme_string.c Wed Apr 30 20:18:56 2008 @@ -867,6 +867,119 @@ /* }}} */ + +/* {{{ proto string grapheme_extractc(string str, bsize[, start]) + Function to extract a sequence of default grapheme clusters up to a character limit */ +PHP_FUNCTION(grapheme_extractc) +{ + char *str, *pstr; + UChar *ustr; + int str_len, ustr_len; + long csize; /* maximum number of characters to return */ + long start = 0; /* starting position in str in bytes */ + UErrorCode status; + unsigned char u_break_iterator_buffer[U_BRK_SAFECLONE_BUFFERSIZE]; + UBreakIterator* bi = NULL; + int ret_pos, pos, prev_pos, prev_ret_pos; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|l", (char **)&str, &str_len, &csize, &start) == FAILURE) { + + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "grapheme_extractb: unable to parse input param", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + if ( start < 0 || start > str_len ) { + + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "grapheme_extractc: start not contained in string", 1 TSRMLS_CC ); + + RETURN_FALSE; + } + + pstr = str + start; + + /* just in case pstr points in the middle of a character, move forward to the start of the next char */ + if ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) { + char *str_end = str + str_len; + + while ( !UTF8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) { + pstr++; + if ( pstr >= str_end ) { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "grapheme_extract: invalid input string", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + } + } + + str_len -= (pstr - str); + + /* convert the string to UTF-16. */ + ustr = NULL; + ustr_len = 0; + status = U_ZERO_ERROR; + intl_convert_utf8_to_utf16(&ustr, &ustr_len, pstr, str_len, &status ); + + if ( U_FAILURE( status ) ) { + /* Set global error code. */ + intl_error_set_code( NULL, status TSRMLS_CC ); + + /* Set error messages. */ + intl_error_set_custom_msg( NULL, "Error converting input string to UTF-16", 1 TSRMLS_CC ); + + if ( NULL != ustr ) + efree( ustr ); + + RETURN_FALSE; + } + + bi = NULL; + status = U_ZERO_ERROR; + bi = grapheme_get_break_iterator(u_break_iterator_buffer, &status TSRMLS_CC ); + + ubrk_setText(bi, ustr, ustr_len, &status); + + pos = 0; + prev_pos = 0; + ret_pos = 0; + prev_ret_pos = 0; + + while ( 1 ) { + pos = ubrk_next(bi); + + if ( UBRK_DONE == pos ) { + break; + } + + /* if we are beyond our limit, then the loop is done */ + if ( pos > csize ) { + break; + } + + /* update our pointer in the original UTF-8 buffer by as many characters + as ubrk_next iterated over */ + + prev_ret_pos = ret_pos; + U8_FWD_N(pstr, ret_pos, str_len, pos - prev_pos); + + if ( prev_ret_pos == ret_pos ) { + /* something wrong - malformed utf8? */ + break; + } + + prev_pos = pos; + } + + efree(ustr); + ubrk_close(bi); + + RETURN_STRINGL(pstr, ret_pos, 1); +} + +/* }}} */ + /* * Local variables: * tab-width: 4 http://cvs.php.net/viewvc.cgi/pecl/intl/tests/grapheme.phpt?r1=1.1&r2=1.2&diff_format=u Index: pecl/intl/tests/grapheme.phpt diff -u pecl/intl/tests/grapheme.phpt:1.1 pecl/intl/tests/grapheme.phpt:1.2 --- pecl/intl/tests/grapheme.phpt:1.1 Fri Apr 4 16:58:13 2008 +++ pecl/intl/tests/grapheme.phpt Wed Apr 30 20:18:56 2008 @@ -589,7 +589,7 @@ //===================================================================================== - $res_str .= "\n" . 'function grapheme_extractB($haystack, $size, $start = 0)' . "\n\n"; + $res_str .= "\n" . 'function grapheme_extractb($haystack, $size, $start = 0)' . "\n\n"; $tests = array( array( "abc", 3, "abc" ), @@ -627,13 +627,13 @@ foreach( $tests as $test ) { $arg0 = urlencode($test[0]); - $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractB"; + $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractb"; if ( 3 == count( $test ) ) { - $result = grapheme_extractB($test[0], $test[1]); + $result = grapheme_extractb($test[0], $test[1]); } else { $res_str .= " starting at byte position $test[2]"; - $result = grapheme_extractB($test[0], $test[1], $test[2]); + $result = grapheme_extractb($test[0], $test[1], $test[2]); } $res_str .= " = "; if ( $result === false ) { @@ -645,6 +645,68 @@ $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n"; } + + //===================================================================================== + $res_str .= "\n" . 'function grapheme_extractc($haystack, $size, $start = 0)' . "\n\n"; + + $tests = array( + array( "abc", 3, "abc" ), + array( "abc", 2, "ab" ), + array( "abc", 1, "a" ), + array( "abc", 0, "" ), + array( "abc" . $char_o_diaeresis_nfd, 0, "" ), + array( "abc" . $char_o_diaeresis_nfd, 1, "a" ), + array( "abc" . $char_o_diaeresis_nfd, 2, "ab" ), + array( "abc" . $char_o_diaeresis_nfd, 3, "abc" ), + array( "abc" . $char_o_diaeresis_nfd, 4, "abc" ), + array( "abc" . $char_o_diaeresis_nfd, 5, "abc" . $char_o_diaeresis_nfd), + array( "abc" . $char_o_diaeresis_nfd, 6, "abc" . $char_o_diaeresis_nfd), + array( $char_o_diaeresis_nfd . "abc", 0, "" ), + array( $char_o_diaeresis_nfd . "abc", 1, "" ), + array( $char_o_diaeresis_nfd . "abc", 2, $char_o_diaeresis_nfd ), + array( $char_o_diaeresis_nfd . "abc", 3, $char_o_diaeresis_nfd . "a" ), + array( $char_o_diaeresis_nfd . "abc", 4, $char_o_diaeresis_nfd . "ab" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 5, $char_o_diaeresis_nfd . "abc" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 6, $char_o_diaeresis_nfd . "abc" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 7, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ), + + array( "abc", 3, 0, "abc" ), + array( "abc", 2, 1, "bc" ), + array( "abc", 1, 2, "c" ), + array( "abc", 0, 3, "" ), + array( "abc", 1, 3, "" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 0, $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "x" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 1, $char_diaeresis . "abc" . $char_a_ring_nfd . "xy" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 2, "abc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 3, "abc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 4, "bc" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 5, "c" . $char_a_ring_nfd . "xyz" ), + array( $char_o_diaeresis_nfd . "abc" . $char_a_ring_nfd . "xyz", 8, 6, $char_a_ring_nfd . "xyz" ), + + ); + + foreach( $tests as $test ) { + $arg0 = urlencode($test[0]); + $res_str .= "extract from \"$arg0\" \"$test[1]\" graphemes - grapheme_extractc"; + if ( 3 == count( $test ) ) { + $result = grapheme_extractc($test[0], $test[1]); + } + else { + $res_str .= " starting at byte position $test[2]"; + $result = grapheme_extractc($test[0], $test[1], $test[2]); + } + $res_str .= " = "; + if ( $result === false ) { + $res_str .= 'false'; + } + else { + $res_str .= urlencode($result); + } + $res_str .= " == " . urlencode($test[count($test)-1]) . check_result($result, $test[count($test)-1]) . "\n"; + } + + //===================================================================================== return $res_str; @@ -998,34 +1060,69 @@ extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 10 = %CC%88 == %CC%88 extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extract starting at byte position 11 = false == false -function grapheme_extractB($haystack, $size, $start = 0) +function grapheme_extractb($haystack, $size, $start = 0) -extract from "abc" "3" graphemes - grapheme_extractB = abc == abc -extract from "abc" "2" graphemes - grapheme_extractB = ab == ab -extract from "abc" "1" graphemes - grapheme_extractB = a == a -extract from "abc" "0" graphemes - grapheme_extractB = == -extract from "a%CC%8Abc" "5" graphemes - grapheme_extractB = a%CC%8Abc == a%CC%8Abc -extract from "a%CC%8Abc" "4" graphemes - grapheme_extractB = a%CC%8Ab == a%CC%8Ab -extract from "a%CC%8Abc" "1" graphemes - grapheme_extractB = == -extract from "a%CC%8Ao%CC%88o%CC%88" "9" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "10" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "11" graphemes - grapheme_extractB = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 -extract from "a%CC%8Ao%CC%88o%CC%88" "6" graphemes - grapheme_extractB = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 -extract from "a%CC%8Ao%CC%88c" "3" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "4" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "5" graphemes - grapheme_extractB = a%CC%8A == a%CC%8A -extract from "a%CC%8Ao%CC%88c" "6" graphemes - grapheme_extractB = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 -extract from "a%CC%8Ao%CC%88c" "7" graphemes - grapheme_extractB = a%CC%8Ao%CC%88c == a%CC%8Ao%CC%88c -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 0 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 2 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 3 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 4 = %CC%88 == %CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractB starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractB starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractB starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractB starting at byte position 8 = o%CC%88 == o%CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractB starting at byte position 10 = %CC%88 == %CC%88 -extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractB starting at byte position 11 = false == false +extract from "abc" "3" graphemes - grapheme_extractb = abc == abc +extract from "abc" "2" graphemes - grapheme_extractb = ab == ab +extract from "abc" "1" graphemes - grapheme_extractb = a == a +extract from "abc" "0" graphemes - grapheme_extractb = == +extract from "a%CC%8Abc" "5" graphemes - grapheme_extractb = a%CC%8Abc == a%CC%8Abc +extract from "a%CC%8Abc" "4" graphemes - grapheme_extractb = a%CC%8Ab == a%CC%8Ab +extract from "a%CC%8Abc" "1" graphemes - grapheme_extractb = == +extract from "a%CC%8Ao%CC%88o%CC%88" "9" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "10" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "11" graphemes - grapheme_extractb = a%CC%8Ao%CC%88o%CC%88 == a%CC%8Ao%CC%88o%CC%88 +extract from "a%CC%8Ao%CC%88o%CC%88" "6" graphemes - grapheme_extractb = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 +extract from "a%CC%8Ao%CC%88c" "3" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "4" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "5" graphemes - grapheme_extractb = a%CC%8A == a%CC%8A +extract from "a%CC%8Ao%CC%88c" "6" graphemes - grapheme_extractb = a%CC%8Ao%CC%88 == a%CC%8Ao%CC%88 +extract from "a%CC%8Ao%CC%88c" "7" graphemes - grapheme_extractb = a%CC%8Ao%CC%88c == a%CC%8Ao%CC%88c +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 0 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 2 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 3 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 4 = %CC%88 == %CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 0 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 2 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "6" graphemes - grapheme_extractb starting at byte position 3 = o%CC%88o%CC%88 == o%CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractb starting at byte position 4 = %CC%88o%CC%88 == %CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "5" graphemes - grapheme_extractb starting at byte position 7 = %CC%88o%CC%88 == %CC%88o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "3" graphemes - grapheme_extractb starting at byte position 8 = o%CC%88 == o%CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractb starting at byte position 10 = %CC%88 == %CC%88 +extract from "o%CC%88o%CC%88o%CC%88o%CC%88" "2" graphemes - grapheme_extractb starting at byte position 11 = false == false + +function grapheme_extractc($haystack, $size, $start = 0) + +extract from "abc" "3" graphemes - grapheme_extractc = abc == abc +extract from "abc" "2" graphemes - grapheme_extractc = ab == ab +extract from "abc" "1" graphemes - grapheme_extractc = a == a +extract from "abc" "0" graphemes - grapheme_extractc = == +extract from "abco%CC%88" "0" graphemes - grapheme_extractc = == +extract from "abco%CC%88" "1" graphemes - grapheme_extractc = a == a +extract from "abco%CC%88" "2" graphemes - grapheme_extractc = ab == ab +extract from "abco%CC%88" "3" graphemes - grapheme_extractc = abc == abc +extract from "abco%CC%88" "4" graphemes - grapheme_extractc = abc == abc +extract from "abco%CC%88" "5" graphemes - grapheme_extractc = abco%CC%88 == abco%CC%88 +extract from "abco%CC%88" "6" graphemes - grapheme_extractc = abco%CC%88 == abco%CC%88 +extract from "o%CC%88abc" "0" graphemes - grapheme_extractc = == +extract from "o%CC%88abc" "1" graphemes - grapheme_extractc = == +extract from "o%CC%88abc" "2" graphemes - grapheme_extractc = o%CC%88 == o%CC%88 +extract from "o%CC%88abc" "3" graphemes - grapheme_extractc = o%CC%88a == o%CC%88a +extract from "o%CC%88abc" "4" graphemes - grapheme_extractc = o%CC%88ab == o%CC%88ab +extract from "o%CC%88abca%CC%8Axyz" "5" graphemes - grapheme_extractc = o%CC%88abc == o%CC%88abc +extract from "o%CC%88abca%CC%8Axyz" "6" graphemes - grapheme_extractc = o%CC%88abc == o%CC%88abc +extract from "o%CC%88abca%CC%8Axyz" "7" graphemes - grapheme_extractc = o%CC%88abca%CC%8A == o%CC%88abca%CC%8A +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax +extract from "abc" "3" graphemes - grapheme_extractc starting at byte position 0 = abc == abc +extract from "abc" "2" graphemes - grapheme_extractc starting at byte position 1 = bc == bc +extract from "abc" "1" graphemes - grapheme_extractc starting at byte position 2 = c == c +extract from "abc" "0" graphemes - grapheme_extractc starting at byte position 3 = == +extract from "abc" "1" graphemes - grapheme_extractc starting at byte position 3 = == +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 0 = o%CC%88abca%CC%8Ax == o%CC%88abca%CC%8Ax +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 1 = %CC%88abca%CC%8Axy == %CC%88abca%CC%8Axy +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 2 = abca%CC%8Axyz == abca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 3 = abca%CC%8Axyz == abca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 4 = bca%CC%8Axyz == bca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 5 = ca%CC%8Axyz == ca%CC%8Axyz +extract from "o%CC%88abca%CC%8Axyz" "8" graphemes - grapheme_extractc starting at byte position 6 = a%CC%8Axyz == a%CC%8Axyz