Skip to contents

cjk_jamo() splits each Hangul syllable into the jamo it is built from; cjk_compose_jamo() puts them back together.

Usage

cjk_jamo(x)

cjk_compose_jamo(x)

Arguments

x

A character vector. Anything else is coerced with as.character(). That coercion is R's, not this package's, so a numeric vector is measured as R chooses to write it – which moves with options(scipen) and options(OutDec), and can therefore differ between sessions. Convert deliberately if you mean to measure numbers; these verbs are for text. A list is not coerced – as.character() deparses one rather than coercing it, so the text measured would be the R code that builds the list – so a list, a data frame or a function is an error naming what to do instead.

Value

cjk_jamo() returns a list the same length as x, each element a character vector of jamo and of the other characters, one per code point; NA gives NA_character_ and the empty string gives character(0). cjk_compose_jamo() takes a character vector and returns one.

Details

A modern Hangul syllable is a composite. Unicode encodes 11,172 of them precomposed in the Hangul Syllables block, each one algorithmically derived from a leading consonant, a vowel and an optional trailing consonant: SIndex = (LIndex * 21 + VIndex) * 28 + TIndex. Both verbs apply that arithmetic directly, so the decomposition is exact: no table can be out of date and no syllable is missed.

Only Hangul is touched

A character that is not a Hangul syllable passes through cjk_jamo() as itself, and cjk_compose_jamo() joins conjoining jamo into syllables and changes nothing else, so both are safe to run over a mixed column. That is deliberately narrower than Unicode normalisation. NFD would also split an accented Latin letter, and NFD and NFC alike replace a CJK compatibility ideograph with its unified form, which in a Korean column means silently rewriting Hanja: the compatibility ideographs exist so that the Korean legacy encodings round-trip. Use cjk_normalize() when normalisation is what you want.

The round trip

cjk_jamo() returns a list, one character vector of jamo per element of x, and cjk_compose_jamo() takes a character vector, so the two do not compose directly: join each element first, as the rt() helper in the examples does. Writing cjk_compose_jamo(cjk_jamo(x)) is an error rather than a silent wrong answer, because as.character() would deparse the list and hand back the literal string c("\u1112", "\u1161", "\u11ab").

Joined and composed, the jamo give back x exactly, with one exception that is the point of cjk_compose_jamo(): conjoining jamo that x already held as separate code points come back composed into their syllable.

That makes jamo the right unit for questions the syllable hides: which initial consonants a corpus favours, whether two spellings differ only in a final consonant, or how to sort by consonant. U+D55C counts three ways, each right for a different question: one character to nchar(), two terminal columns to cjk_width() (a Hangul syllable is East Asian Wide), and three jamo here.

See also

cjk_script() to detect Hangul, cjk_blocks() for the blocks involved, cjk_normalize() for the Unicode normalisation forms.

Examples

# U+D55C U+AE00, "Hangul"
cjk_jamo("\ud55c\uae00")
#> [[1]]
#> [1] "ᄒ" "ᅡ"   "ᆫ"   "ᄀ" "ᅳ"   "ᆯ"  
#> 

# join each element and compose it: the round trip is exact
rt <- function(x) {
  cjk_compose_jamo(vapply(cjk_jamo(x), paste, character(1), collapse = ""))
}
rt("\ud55c\uae00")
#> [1] "한글"

# text that is not Hangul is left alone: the accent stays where it was,
# and the compatibility ideograph U+F900 is not swapped for U+8C48
cjk_jamo("e\u0301")
#> [[1]]
#> [1] "e" "́" 
#> 
rt("\uf900") == "\uf900"
#> [1] TRUE