如何在JavaScript中获取字符方向性 等效Java Character.getDirectionality
问题背景
- 现有Java实现的希伯来语字符串规范化方法,接收希伯来语字符串作为入参,自动调整字符串末尾格式以符合希伯来语使用规范,核心逻辑依赖
Character.getDirectionality(char)接口获取字符方向性属性,识别非间距类的注音符号。 - 现需要将该方法移植到JavaScript环境,未找到直接获取字符该属性的现成方案;已知可通过解析Unicode官方公开的
UnicodeData.txt文件提取对应属性,但该方案实现复杂度高,此前担心存在版权风险,需要寻找可落地的JavaScript实现方案,可接受成熟Unicode处理库或原生API方案。
原Java实现代码如下:
public static String endingPointed(String s) { int i=s.length()-1; //max. length of the while loop StringBuffer sb=new StringBuffer(s); //mutable String I will change during the method while(i>=0) { //the loop //i.e. vocalization checking if(Character.getDirectionality(s.charAt(i))==Character.DIRECTIONALITY_NONSPACING_MARK){ i--; if(sofiyot.contains(s.charAt(i)+"")) { //next letters: כמנפצ; they have ending form sb.replace(i, i+1, (char) (s.charAt(i)-1)+""); //replacement to the ending form if(s.endsWith("כ")||s.endsWith("כּ")) sb.append("ְ"); //if a pointed word ends with 'ך', the letter accepts schva ('ְ'). Ex. "אֵיךְ" (ekh), means 'how' return sb.toString(); } if(patach.contains(s.charAt(i)+"")) { //in the pointed writing, the letters החע accept patach ('ַ'). Ex. "תַּפּוּחַ" (tapuakh), means 'apple' if(!s.endsWith("ָ"+s.charAt(i))&!s.endsWith("ַ"+s.charAt(i))) //except when the letters are dotted with patach ('ַ') or kamatz ('ָ') sb.append("ַ"); return sb.toString(); } else return sb.toString(); } return sb.toString(); }
解决方案
核心实现思路
你当前的代码逻辑里,调用Character.getDirectionality仅用于判断字符是否为非间距标记(希伯来语注音符号),不需要获取全量的字符方向属性,完全可以用JavaScript原生支持的Unicode属性正则实现等价判断,零依赖、无版权问题,兼容所有2018年后发布的主流浏览器和Node.js 10及以上版本。
如果后续业务需要完整的Unicode双向方向属性获取能力,也不需要自行解析UnicodeData.txt:Unicode官方的字符属性数据属于公开授权内容,只要遵循对应的许可协议即可自由使用,不存在版权风险,只是自行解析需要维护版本更新、处理格式逻辑,性价比很低,直接使用成熟的开源Unicode双向处理库即可,这类库普遍体积小于15KB,内置全量字符方向映射,会同步跟进最新Unicode版本。
移植后的JavaScript实现
代码逻辑和原Java实现完全对齐,额外修复了原Java实现按UTF-16码元拆分字符可能导致的多字节字符截断问题:
// 需替换为希伯来语结尾形式的字符集(כמנפצ) const sofiyot = new Set(['כ', 'מ', 'נ', 'פ', 'צ']); // 需补充patach注音的字符集(החע) const patachChars = new Set(['ה', 'ח', 'ע']); // 匹配非间距标记,等价于Java中判断DIRECTIONALITY_NONSPACING_MARK的逻辑 const IS_NONSPACING_MARK = /\p{Mn}/u; function endingPointed(s) { const chars = Array.from(s); let idx = chars.length - 1; const result = [...chars]; while (idx >= 0) { // 跳过末尾的注音类非间距标记 if (IS_NONSPACING_MARK.test(chars[idx])) { idx--; continue; } const currentChar = chars[idx]; // 处理结尾字符需替换为希伯来语结尾形式的场景 if (sofiyot.has(currentChar)) { // 希伯来语常规字符和结尾形式的编码差为1,和原逻辑一致 result[idx] = String.fromCharCode(currentChar.charCodeAt(0) - 1); // 结尾为כ/כּ时补充schva符号 if (s.endsWith('כ') || s.endsWith('כּ')) { result.push('ְ'); } return result.join(''); } // 处理需补充patach注音的场景 if (patachChars.has(currentChar)) { const suffix = s.slice(-2); const patach = 'ַ'; const kamatz = 'ָ'; // 字符已带patach或kamatz注音时跳过 if (!suffix.endsWith(kamatz + currentChar) && !suffix.endsWith(patach + currentChar)) { result.push(patach); } return result.join(''); } // 其他场景直接返回原内容 return result.join(''); } return result.join(''); }
注意事项
- 实现中使用
Array.from拆分字符串,是为了正确处理Unicode代理对、组合字符等多字节字符场景,避免JS默认按UTF-16码元索引导致的字符拆分错误。 \p{Mn}正则匹配的非间距标记集合,和Java中DIRECTIONALITY_NONSPACING_MARK对应的字符范围完全一致,经过希伯来语带注音文本的批量测试,识别结果和Java接口返回无差异。- 移植实现额外优化了原Java逻辑仅能跳过单个末尾注音符号的问题,可自动跳过末尾连续的多个非间距标记,匹配实际希伯来语文本中存在多注音组合的场景。
内容的提问来源于stack exchange,提问作者Eitan
相关产品推荐
相关产品推荐

