Commit 218c39b1 authored by Skylot's avatar Skylot

core: option for control escaping of unicode characters (#103)

parent e915f4fc
......@@ -42,20 +42,22 @@ Run **jadx** on itself:
```
jadx[-gui] [options] <input file> (.dex, .apk, .jar or .class)
options:
-d, --output-dir - output directory
-j, --threads-count - processing threads count
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
-r, --no-res - do not decode resources
-s, --no-src - do not decompile source code
--show-bad-code - show inconsistent code (incorrectly decompiled)
--cfg - save methods control flow graph to dot file
--raw-cfg - save methods control flow graph (use raw instructions)
-v, --verbose - verbose output
--deobf - activate deobfuscation
--deobf-min - min length of name
--deobf-max - max length of name
--deobf-rewrite-cfg - force to save deobfuscation map
-h, --help - print this help
-d, --output-dir - output directory
-j, --threads-count - processing threads count
-f, --fallback - make simple dump (using goto instead of 'if', 'for', etc)
-r, --no-res - do not decode resources
-s, --no-src - do not decompile source code
--show-bad-code - show inconsistent code (incorrectly decompiled)
--cfg - save methods control flow graph to dot file
--raw-cfg - save methods control flow graph (use raw instructions)
-v, --verbose - verbose output
--deobf - activate deobfuscation
--deobf-min - min length of name
--deobf-max - max length of name
--deobf-rewrite-cfg - force to save deobfuscation map
--deobf-use-sourcename - use source file name as class name alias
--escape-unicode - escape non latin characters in strings (with \u)
-h, --help - print this help
Example:
jadx -d out classes.dex
```
......
package jadx.cli;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import jadx.api.IJadxArgs;
import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxException;
......@@ -67,6 +69,9 @@ public class JadxCLIArgs implements IJadxArgs {
@Parameter(names = {"--deobf-use-sourcename"}, description = "use source file name as class name alias")
protected boolean deobfuscationUseSourceNameAsAlias = false;
@Parameter(names = {"--escape-unicode"}, description = "escape non latin characters in strings (with \\u)")
protected boolean escapeUnicode = false;
@Parameter(names = {"-h", "--help"}, description = "print this help", help = true)
protected boolean printHelp = false;
......@@ -117,7 +122,10 @@ public class JadxCLIArgs implements IJadxArgs {
ch.qos.logback.classic.Logger rootLogger =
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
// remove INFO ThresholdFilter
rootLogger.getAppender("STDOUT").clearAllFilters();
Appender<ILoggingEvent> appender = rootLogger.getAppender("STDOUT");
if (appender != null) {
appender.clearAllFilters();
}
}
} catch (JadxException e) {
System.err.println("ERROR: " + e.getMessage());
......@@ -251,4 +259,9 @@ public class JadxCLIArgs implements IJadxArgs {
public boolean useSourceNameAsClassAlias() {
return deobfuscationUseSourceNameAsAlias;
}
@Override
public boolean escapeUnicode() {
return escapeUnicode;
}
}
......@@ -30,4 +30,6 @@ public interface IJadxArgs {
boolean isDeobfuscationForceSave();
boolean useSourceNameAsClassAlias();
boolean escapeUnicode();
}
......@@ -24,6 +24,8 @@ public class JadxArgs implements IJadxArgs {
private int deobfuscationMinLength = 0;
private int deobfuscationMaxLength = Integer.MAX_VALUE;
private boolean escapeUnicode = false;
@Override
public File getOutDir() {
return outDir;
......@@ -149,4 +151,13 @@ public class JadxArgs implements IJadxArgs {
public void setDeobfuscationMaxLength(int deobfuscationMaxLength) {
this.deobfuscationMaxLength = deobfuscationMaxLength;
}
@Override
public boolean escapeUnicode() {
return escapeUnicode;
}
public void setEscapeUnicode(boolean escapeUnicode) {
this.escapeUnicode = escapeUnicode;
}
}
......@@ -130,11 +130,11 @@ public class AnnotationGen {
return;
}
if (val instanceof String) {
code.add(StringUtils.unescapeString((String) val));
code.add(getStringUtils().unescapeString((String) val));
} else if (val instanceof Integer) {
code.add(TypeGen.formatInteger((Integer) val));
} else if (val instanceof Character) {
code.add(StringUtils.unescapeChar((Character) val));
code.add(getStringUtils().unescapeChar((Character) val));
} else if (val instanceof Boolean) {
code.add(Boolean.TRUE.equals(val) ? "true" : "false");
} else if (val instanceof Float) {
......@@ -172,4 +172,8 @@ public class AnnotationGen {
throw new JadxRuntimeException("Can't decode value: " + val + " (" + val.getClass() + ")");
}
}
private StringUtils getStringUtils() {
return cls.dex().root().getStringUtils();
}
}
......@@ -348,7 +348,7 @@ public class ClassGen {
if (fv != null) {
code.add(" = ");
if (fv.getValue() == null) {
code.add(TypeGen.literalToString(0, f.getType()));
code.add(TypeGen.literalToString(0, f.getType(), cls));
} else {
if (fv.getValueType() == InitType.CONST) {
annotationGen.encodeValue(code, fv.getValue());
......
......@@ -38,7 +38,6 @@ import jadx.core.dex.nodes.MethodNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.RegionUtils;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.CodegenException;
import jadx.core.utils.exceptions.JadxRuntimeException;
......@@ -130,8 +129,8 @@ public class InsnGen {
code.add(mgen.getNameGen().assignArg(arg));
}
private static String lit(LiteralArg arg) {
return TypeGen.literalToString(arg.getLiteral(), arg.getType());
private String lit(LiteralArg arg) {
return TypeGen.literalToString(arg.getLiteral(), arg.getType(), mth);
}
private void instanceField(CodeWriter code, FieldInfo field, InsnArg arg) throws CodegenException {
......@@ -236,7 +235,7 @@ public class InsnGen {
switch (insn.getType()) {
case CONST_STR:
String str = ((ConstStringNode) insn).getString();
code.add(StringUtils.unescapeString(str));
code.add(mth.dex().root().getStringUtils().unescapeString(str));
break;
case CONST_CLASS:
......
......@@ -255,7 +255,7 @@ public class RegionGen extends InsnGen {
}
}
} else if (k instanceof Integer) {
code.add(TypeGen.literalToString((Integer) k, arg.getType()));
code.add(TypeGen.literalToString((Integer) k, arg.getType(), mth));
} else {
throw new JadxRuntimeException("Unexpected key in switch: " + (k != null ? k.getClass() : null));
}
......
package jadx.core.codegen;
import jadx.api.JadxArgs;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.dex.instructions.args.PrimitiveType;
import jadx.core.dex.nodes.IDexNode;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
......@@ -31,7 +33,16 @@ public class TypeGen {
*
* @throws JadxRuntimeException for incorrect type or literal value
*/
public static String literalToString(long lit, ArgType type, IDexNode dexNode) {
return literalToString(lit, type, dexNode.root().getStringUtils());
}
@Deprecated
public static String literalToString(long lit, ArgType type) {
return literalToString(lit, type, new StringUtils(new JadxArgs()));
}
private static String literalToString(long lit, ArgType type, StringUtils stringUtils) {
if (type == null || !type.isTypeKnown()) {
String n = Long.toString(lit);
if (Math.abs(lit) > 100) {
......@@ -46,7 +57,7 @@ public class TypeGen {
case BOOLEAN:
return lit == 0 ? "false" : "true";
case CHAR:
return StringUtils.unescapeChar((char) lit);
return stringUtils.unescapeChar((char) lit);
case BYTE:
return formatByte((byte) lit);
case SHORT:
......
......@@ -43,7 +43,7 @@ import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dx.rop.code.AccessFlags;
public class ClassNode extends LineAttrNode implements ILoadable {
public class ClassNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);
private final DexNode dex;
......@@ -472,10 +472,16 @@ public class ClassNode extends LineAttrNode implements ILoadable {
return accessFlags;
}
@Override
public DexNode dex() {
return dex;
}
@Override
public RootNode root() {
return dex.root();
}
public String getRawName() {
return clsInfo.getRawName();
}
......
......@@ -28,7 +28,7 @@ import com.android.dex.MethodId;
import com.android.dex.ProtoId;
import com.android.dex.TypeList;
public class DexNode {
public class DexNode implements IDexNode {
public static final int NO_INDEX = -1;
......@@ -210,11 +210,17 @@ public class DexNode {
return dexBuf.open(offset);
}
@Override
public RootNode root() {
return root;
}
@Override
public DexNode dex() {
return this;
}
@Override
public String toString() {
return "DEX";
}
......
package jadx.core.dex.nodes;
public interface IDexNode {
DexNode dex();
RootNode root();
}
......@@ -44,7 +44,7 @@ import com.android.dex.Code;
import com.android.dex.Code.CatchHandler;
import com.android.dex.Code.Try;
public class MethodNode extends LineAttrNode implements ILoadable {
public class MethodNode extends LineAttrNode implements ILoadable, IDexNode {
private static final Logger LOG = LoggerFactory.getLogger(MethodNode.class);
private final MethodInfo mthInfo;
......@@ -594,10 +594,16 @@ public class MethodNode extends LineAttrNode implements ILoadable {
this.region = region;
}
@Override
public DexNode dex() {
return parentClass.dex();
}
@Override
public RootNode root() {
return dex().root();
}
public MethodInfo getMethodInfo() {
return mthInfo;
}
......
......@@ -7,6 +7,7 @@ import jadx.api.ResourcesLoader;
import jadx.core.clsp.ClspGraph;
import jadx.core.dex.info.ClassInfo;
import jadx.core.utils.ErrorsCounter;
import jadx.core.utils.StringUtils;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxException;
import jadx.core.utils.files.DexFile;
......@@ -31,6 +32,7 @@ public class RootNode {
private final ErrorsCounter errorsCounter = new ErrorsCounter();
private final IJadxArgs args;
private final StringUtils stringUtils;
private List<DexNode> dexNodes;
private Map<Integer, String> resourcesNames = new HashMap<Integer, String>();
......@@ -41,6 +43,7 @@ public class RootNode {
public RootNode(IJadxArgs args) {
this.args = args;
this.stringUtils = new StringUtils(args);
}
public void load(List<InputFile> inputFiles) throws DecodeException {
......@@ -194,4 +197,8 @@ public class RootNode {
public IJadxArgs getArgs() {
return args;
}
public StringUtils getStringUtils() {
return stringUtils;
}
}
package jadx.core.utils;
import jadx.api.IJadxArgs;
public class StringUtils {
private StringUtils() {
private final boolean escapeUnicode;
public StringUtils(IJadxArgs args) {
this.escapeUnicode = args.escapeUnicode();
}
public static String unescapeString(String str) {
public String unescapeString(String str) {
int len = str.length();
if (len == 0) {
return "\"\"";
}
StringBuilder res = new StringBuilder();
for (int i = 0; i < len; i++) {
int c = str.charAt(i) & 0xFFFF;
processChar(c, res);
......@@ -16,7 +23,7 @@ public class StringUtils {
return '"' + res.toString() + '"';
}
public static String unescapeChar(char ch) {
public String unescapeChar(char ch) {
if (ch == '\'') {
return "'\\\''";
}
......@@ -25,7 +32,7 @@ public class StringUtils {
return '\'' + res.toString() + '\'';
}
private static void processChar(int c, StringBuilder res) {
private void processChar(int c, StringBuilder res) {
switch (c) {
case '\n': res.append("\\n"); break;
case '\r': res.append("\\r"); break;
......@@ -37,10 +44,10 @@ public class StringUtils {
case '\\': res.append("\\\\"); break;
default:
if (32 <= c && c <= 126) {
res.append((char) c);
} else {
if (c < 32 || c >= 127 && escapeUnicode) {
res.append("\\u").append(String.format("%04x", c));
} else {
res.append((char) c);
}
break;
}
......
package jadx.tests
import jadx.api.JadxArgs
import jadx.core.utils.StringUtils
import spock.lang.Specification
class TestStringUtils extends Specification {
def "unescape string"() {
def args = new JadxArgs()
args.setEscapeUnicode(true)
def stringUtils = new StringUtils(args)
expect:
StringUtils.unescapeString(input) == "\"$expected\""
stringUtils.unescapeString(input) == "\"$expected\""
where:
input | expected
......@@ -26,12 +30,14 @@ class TestStringUtils extends Specification {
def "unescape char"() {
expect:
StringUtils.unescapeChar(input as char) == "'$expected'"
new StringUtils(new JadxArgs()).unescapeChar(input as char) == "'$expected'"
where:
input | expected
'a' | "a"
' ' | " "
'\n' | "\\n"
'\'' | "\\\'"
'\0' | "\\u0000"
}
}
......@@ -5,7 +5,7 @@ mainClassName = 'jadx.gui.JadxGUI'
dependencies {
compile(project(":jadx-core"))
compile(project(":jadx-cli"))
compile 'com.fifesoft:rsyntaxtextarea:2.5.7'
compile 'com.fifesoft:rsyntaxtextarea:2.5.8'
compile 'com.google.code.gson:gson:2.3.1'
compile files('libs/jfontchooser-1.0.5.jar')
compile 'com.googlecode.concurrent-trees:concurrent-trees:2.4.0'
......
......@@ -30,7 +30,7 @@ public class JadxGUI {
}
});
} catch (Throwable e) {
LOG.error("Error: {}", e.getMessage());
LOG.error("Error: {}", e.getMessage(), e);
System.exit(1);
}
}
......
......@@ -165,6 +165,10 @@ public class JadxSettings extends JadxCLIArgs {
this.deobfuscationUseSourceNameAsAlias = useSourceNameAsAlias;
}
public void setEscapeUnicode(boolean escapeUnicode) {
this.escapeUnicode = escapeUnicode;
}
public boolean isUseFastSearch() {
return false;
// return useFastSearch;
......
......@@ -67,6 +67,8 @@ public class JadxSettingsWindow extends JDialog {
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(makeDeobfuscationGroup());
panel.add(makeDecompilationGroup());
panel.add(makeEditorGroup());
panel.add(makeOtherGroup());
JButton saveBtn = new JButton(NLS.str("preferences.save"));
......@@ -179,15 +181,29 @@ public class JadxSettingsWindow extends JDialog {
return deobfGroup;
}
private SettingsGroup makeOtherGroup() {
JCheckBox update = new JCheckBox();
update.setSelected(settings.isCheckForUpdates());
update.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
private SettingsGroup makeEditorGroup() {
JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
fontBtn.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
JFontChooser fontChooser = new JFontChooser();
fontChooser.setSelectedFont(settings.getFont());
int result = fontChooser.showDialog(JadxSettingsWindow.this);
if (result == JFontChooser.OK_OPTION) {
Font font = fontChooser.getSelectedFont();
LOG.info("Selected Font : {}", font);
settings.setFont(font);
mainWindow.updateFont(font);
}
}
});
SettingsGroup other = new SettingsGroup(NLS.str("preferences.editor"));
other.addRow(NLS.str("preferences.font"), fontBtn);
return other;
}
private SettingsGroup makeDecompilationGroup() {
JCheckBox fallback = new JCheckBox();
fallback.setSelected(settings.isFallbackMode());
fallback.addItemListener(new ItemListener() {
......@@ -224,6 +240,42 @@ public class JadxSettingsWindow extends JDialog {
}
});
JCheckBox autoStartJobs = new JCheckBox();
autoStartJobs.setSelected(settings.isAutoStartJobs());
autoStartJobs.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED);
}
});
JCheckBox escapeUnicode = new JCheckBox();
escapeUnicode.setSelected(settings.escapeUnicode());
escapeUnicode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setEscapeUnicode(e.getStateChange() == ItemEvent.SELECTED);
needReload();
}
});
SettingsGroup other = new SettingsGroup(NLS.str("preferences.decompile"));
other.addRow(NLS.str("preferences.threads"), threadsCount);
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
other.addRow(NLS.str("preferences.escapeUnicode"), escapeUnicode);
other.addRow(NLS.str("preferences.fallback"), fallback);
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
return other;
}
private SettingsGroup makeOtherGroup() {
JCheckBox update = new JCheckBox();
update.setSelected(settings.isCheckForUpdates());
update.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setCheckForUpdates(e.getStateChange() == ItemEvent.SELECTED);
}
});
JCheckBox cfg = new JCheckBox();
cfg.setSelected(settings.isCFGOutput());
cfg.addItemListener(new ItemListener() {
......@@ -242,30 +294,6 @@ public class JadxSettingsWindow extends JDialog {
}
});
JButton fontBtn = new JButton(NLS.str("preferences.select_font"));
fontBtn.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
JFontChooser fontChooser = new JFontChooser();
fontChooser.setSelectedFont(settings.getFont());
int result = fontChooser.showDialog(JadxSettingsWindow.this);
if (result == JFontChooser.OK_OPTION) {
Font font = fontChooser.getSelectedFont();
LOG.info("Selected Font : {}", font);
settings.setFont(font);
mainWindow.updateFont(font);
}
}
});
JCheckBox autoStartJobs = new JCheckBox();
autoStartJobs.setSelected(settings.isAutoStartJobs());
autoStartJobs.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
settings.setAutoStartJobs(e.getStateChange() == ItemEvent.SELECTED);
}
});
JCheckBox fastSearch = new JCheckBox();
fastSearch.setEnabled(false);
fastSearch.setSelected(settings.isUseFastSearch());
......@@ -277,15 +305,9 @@ public class JadxSettingsWindow extends JDialog {
SettingsGroup other = new SettingsGroup(NLS.str("preferences.other"));
other.addRow(NLS.str("preferences.check_for_updates"), update);
other.addRow(NLS.str("preferences.threads"), threadsCount);
other.addRow(NLS.str("preferences.fallback"), fallback);
other.addRow(NLS.str("preferences.showInconsistentCode"), showInconsistentCode);
other.addRow(NLS.str("preferences.skipResourcesDecode"), resourceDecode);
other.addRow(NLS.str("preferences.cfg"), cfg);
other.addRow(NLS.str("preferences.raw_cfg"), rawCfg);
other.addRow(NLS.str("preferences.font"), fontBtn);
other.addRow(NLS.str("preferences.fast_search"), fastSearch);
other.addRow(NLS.str("preferences.start_jobs"), autoStartJobs);
return other;
}
......
......@@ -52,10 +52,13 @@ usage_dialog.label=Usage for:
preferences.title=Preferences
preferences.deobfuscation=Deobfuscation
preferences.editor=Editor
preferences.decompile=Decompilation
preferences.other=Other
preferences.check_for_updates=Check for updates on startup
preferences.fallback=Fallback (simple dump)
preferences.fallback=Fallback mode (simple dump)
preferences.showInconsistentCode=Show inconsistent code
preferences.escapeUnicode=Escape unicode
preferences.skipResourcesDecode=Don't decode resources
preferences.threads=Processing threads count
preferences.cfg=Generate methods CFG graphs (in 'dot' format)
......
package jadx.samples;
import java.util.ArrayList;
import java.util.List;
public class TestUnicode extends AbstractTest {
/**
* Some unicode strings from:
* http://www.ltg.ed.ac.uk/~richard/unicode-sample-3-2.html
*/
public List<String> strings() {
List<String> list = new ArrayList<String>();
list.add("! \" # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \\ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~");
list.add("¡ ¢ £ ¤ ¥ ¦ § ¨ © ª « ¬ ® ¯ ° ± ² ³ ´ µ ¶ · ¸ ¹ º » ¼ ½ ¾ ¿ À Á Â Ã Ä Å Æ Ç È É Ê Ë Ì Í Î Ï Ð Ñ Ò Ó Ô Õ Ö × Ø Ù Ú Û Ü Ý Þ ß à á â ã ä å æ ç è é ê ë ì í î ï ð ñ ò ó ô õ ö ÷ ø ù ú û ü ý þ ÿ");
list.add("∀ ∁ ∂ ∃ ∄ ∅ ∆ ∇ ∈ ∉ ∊ ∋ ∌ ∍ ∎ ∏ ∐ ∑ − ∓ ∔ ∕ ∖ ∗ ∘ ∙ √ ∛ ∜ ∝ ∞ ∟ ∠ ∡ ∢ ∣ ∤ ∥ ∦ ∧ ∨ ∩ ∪ ∫ ∬ ∭ ∮ ∯ ∰ ∱ ∲ ∳ ∴ ∵ ∶ ∷ ∸ ∹ ∺ ∻ ∼ ∽ ∾ ∿ ≀ ≁ ≂ ≃ ≄ ≅ ≆ ≇ ≈ ≉ ≊ ≋ ≌ ≍ ≎ ≏ ≐ ≑ ≒ ≓ ≔ ≕ ≖ ≗ ≘ ≙ ≚ ≛ ≜ ≝ ≞ ≟ ≠ ≡ ≢ ≣ ≤ ≥ ≦ ≧ ≨ ≩ ≪ ≫ ≬ ≭ ≮ ≯ ≰ ≱ ≲ ≳ ≴ ≵ ≶ ≷ ≸ ≹ ≺ ≻ ≼ ≽ ≾ ≿");
list.add("Ѐ Ё Ђ Ѓ Є Ѕ І Ї Ј Љ Њ Ћ Ќ Ѝ Ў Џ А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я а б в г д е ж з и й к л м н о п р с т у ф х ц ч ш щ ъ ы ь э ю я ѐ ё ђ ѓ є ѕ і ї");
list.add("ぁ あ ぃ い ぅ う ぇ え ぉ お か が き ぎ く ぐ け げ こ ご さ ざ し じ す ず せ ぜ そ ぞ た だ ち ぢ っ つ づ て で と ど な に ぬ ね の は ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ ま み む め も ゃ や ゅ ゆ ょ よ ら り る れ ろ ゎ わ ゐ ゑ を ん");
list.add("ァ ア ィ イ ゥ ウ ェ エ ォ オ カ ガ キ ギ ク グ ケ ゲ コ ゴ サ ザ シ ジ ス ズ セ ゼ ソ ゾ タ ダ チ ヂ ッ ツ ヅ テ デ ト ド ナ ニ ヌ ネ ノ ハ バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ マ ミ ム メ モ ャ ヤ ュ ユ ョ ヨ ラ リ ル レ ロ ヮ ワ ヰ ヱ ヲ ン ヴ ヵ ヶ");
list.add("一 丁 丂 七 丄 丅 丆 万 丈 三 上 下 丌 不 与 丏 丐 丑 丒 专 且 丕 世 丗 丘 丙 业 丛 东 丝 丞 丟 丠 両 丢 丣 两 严 並 丧 丨 丩 个 丫 丬 中 丮 丯 丰 丱 串 丳 临 丵 丶 丷 丸 丹 为 主 丼 丽 举 丿 乀 乁 乂 乃 乄 久 乆 乇 么 义 乊 之 乌 乍 乎 乏 乐 乑 乒 乓 乔 乕 乖 乗 乘 乙 乚 乛 乜 九 乞 也 习 乡 乢 乣 乤 乥 书 乧 乨 乩 乪 乫 乬 乭 乮 乯 买 乱 乲 乳 乴 乵 乶 乷 乸 乹 乺 乻 乼 乽 乾 乿");
list.add("豈 更 車 賈 滑 串 句 龜 龜 契 金 喇 奈 懶 癩 羅 蘿 螺 裸 邏 樂 洛 烙 珞 落 酪 駱 亂 卵 欄 爛 蘭 鸞 嵐 濫 藍 襤 拉 臘 蠟 廊 朗 浪 狼 郎 來 冷 勞 擄 櫓 爐 盧 老 蘆 虜 路 露 魯 鷺 碌 祿 綠 菉 錄 鹿 論 壟 弄 籠 聾 牢 磊 賂 雷 壘 屢 樓 淚 漏 累 縷 陋 勒 肋 凜 凌 稜 綾 菱 陵 讀 拏 樂 諾 丹 寧 怒 率 異 北 磻 便 復 不 泌 數 索 參 塞 省 葉 說 殺 辰 沈 拾 若 掠 略 亮 兩 凉 梁 糧 良 諒 量 勵");
list.add("ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄸ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅃ ㅄ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ ㅤ ㅥ ㅦ ㅧ ㅨ ㅩ ㅪ ㅫ ㅬ ㅭ ㅮ ㅯ ㅰ ㅱ ㅲ ㅳ ㅴ ㅵ ㅶ ㅷ ㅸ ㅹ ㅺ ㅻ ㅼ ㅽ ㅾ ㅿ ㆀ ㆁ ㆂ ㆃ ㆄ ㆅ ㆆ ㆇ ㆈ ㆉ ㆊ ㆋ ㆌ");
list.add("가 각 갂 갃 간 갅 갆 갇 갈 갉 갊 갋 갌 갍 갎 갏 감 갑 값 갓 갔 강 갖 갗 갘 같 갚 갛 개 객 갞 갟 갠 갡 갢 갣 갤 갥 갦 갧 갨 갩 갪 갫 갬 갭 갮 갯 갰 갱 갲 갳 갴 갵 갶 갷 갸 갹 갺 갻 갼 갽 갾 갿 걀 걁 걂 걃 걄 걅 걆 걇 걈 걉 걊 걋 걌 걍 걎 걏 걐 걑 걒 걓 걔 걕 걖 걗 걘 걙 걚 걛 걜 걝 걞 걟 걠 걡 걢 걣 걤 걥 걦 걧 걨 걩 걪 걫 걬 걭 걮 걯 거 걱 걲 걳 건 걵 걶 걷 걸 걹 걺 걻 걼 걽 걾 걿");
list.add( "؛ ؟ ء آ أ ؤ إ ئ ا ب ة ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ـ ف ق ك ل م ن ه و ى ي ً ٌ ٍ َ ُ ِ ّ ْ ٓ ٔ ٕ ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩ ٪ ٫ ٬ ٭ ٮ ٯ ٰ ٱ ٲ ٳ ٴ ٵ ٶ ٷ ٸ ٹ ٺ ٻ ټ ٽ پ ٿ ڀ ځ ڂ ڃ ڄ څ چ ڇ ڈ ډ ڊ ڋ ڌ ڍ ڎ ڏ ڐ ڑ ڒ ړ ڔ ڕ ږ ڗ ژ ڙ ښ ڛ ڜ ڝ ڞ ڟ ڠ ڡ ڢ ڣ ڤ ڥ ڦ ڧ ڨ ک ڪ ګ ڬ");
list.add( "ஃ அ ஆ இ ஈ உ ஊ எ ஏ ஐ ஒ ஓ ஔ க ங ச ஜ ஞ ட ண த ந ன ப ம ய ர ற ல ள ழ வ ஷ ஸ ஹ ா ி ீ ு ூ ெ ே ை ொ ோ ௌ ் ௗ ௧ ௨ ௩ ௪ ௫ ௬ ௭ ௮ ௯ ௰ ௱ ௲");
list.add( "\uD800\uDF00 \uD800\uDF01 \uD800\uDF02 \uD800\uDF03 \uD800\uDF04 \uD800\uDF05 \uD800\uDF06 \uD800\uDF07 \uD800\uDF08 \uD800\uDF09 \uD800\uDF0A \uD800\uDF0B \uD800\uDF0C \uD800\uDF0D \uD800\uDF0E \uD800\uDF0F \uD800\uDF10 \uD800\uDF11 \uD800\uDF12 \uD800\uDF13 \uD800\uDF14 \uD800\uDF15 \uD800\uDF16 \uD800\uDF17 \uD800\uDF18 \uD800\uDF19 \uD800\uDF1A \uD800\uDF1B \uD800\uDF1C \uD800\uDF1D \uD800\uDF1E \uD800\uDF20 \uD800\uDF21 \uD800\uDF22 \uD800\uDF23");
list.add("𐌀 𐌁 𐌂 𐌃 𐌄 𐌅 𐌆 𐌇 𐌈 𐌉 𐌊 𐌋 𐌌 𐌍 𐌎 𐌏 𐌐 𐌑 𐌒 𐌓 𐌔 𐌕 𐌖 𐌗 𐌘 𐌙 𐌚 𐌛 𐌜 𐌝 𐌞 𐌠 𐌡 𐌢 𐌣");
return list;
}
@Override
public boolean testRun() throws Exception {
String s = "\uD800\uDF0F"; // 𐌏
int codePoint = s.codePointAt(0);
System.out.println(s + " = " + codePoint);
assertEquals(codePoint, 66319);
return true;
}
public static void main(String[] args) throws Exception {
new TestUnicode().testRun();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment