将扩展ASCII或Unicode转换为7位ASCII(〈128)等效值,包括特殊字符

wtlkbnrh  于 2022-11-07  发布在  DB2
关注(0)|答案(3)|浏览(199)

如何将Java中的字符从扩展ASCII或Unicode转换为它们的7位ASCII等价物,包括诸如open之类的特殊字符( 0x 93)并关闭( 0x 94)引号转换为简单双引号(" 0x 22)。或类似的破折号( 0x 96)转换为连字符-减号(- 0x 2d).我已经发现Stack Overflow questions与此类似,但答案似乎只处理口音,忽略了特殊字符。
例如,我想将“Caffè – Peña”转换为"Caffe - Pena"
然而当我使用java.text.Normalizer时:

String sample = "“Caffè – Peña”";
System.out.println(Normalizer.normalize(sample, Normalizer.Form.NFD)
                         .replaceAll("\\p{InCombiningDiacriticalMarks}", ""));

输出为

“Caffe – Pena”

为了阐明我的需求,我正在与一个使用EBCDIC编码的IBM i Db2数据库交互。例如,如果用户粘贴一个从Word或Outlook复制的字符串,类似于我指定的字符将被转换为SUB(在EBCDIC中为0x 3F,在ASCII中为0x 1A)。这会导致许多不必要的麻烦。我正在寻找一种方法来净化字符串,以便尽可能少地丢失信息。

vktxenjb

vktxenjb1#

您可以按照另一位评论者的建议,使用String.replace()替换引号字符,并且随着时间的推移,问题字符的列表可能会越来越多。
您也可以使用更通用的函数来替换或忽略任何无法编码的字符。例如:

private String removeUnrepresentableChars(final String _str, final String _encoding) throws CharacterCodingException, UnsupportedEncodingException {
        final CharsetEncoder enccoder = Charset.forName(_encoding).newEncoder();
        enccoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
        ByteBuffer encoded = enccoder.encode(CharBuffer.wrap(_str));
        return new String(encoded.array(), _encoding);
    }

    private String replaceUnrepresentableChars(final String _str, final String _encoding, final String _replacement) throws CharacterCodingException, UnsupportedEncodingException {
        final CharsetEncoder encoder = Charset.forName(_encoding).newEncoder();
        encoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
        encoder.replaceWith(_replacement.getBytes(_encoding));
        ByteBuffer encoded = encoder.encode(CharBuffer.wrap(_str));
        return new String(encoded.array(), _encoding);
    }

例如,你可以把_encoding称为“IBM-037”。
但是,如果您的目标是尽可能少地丢失信息,则应评估数据是否可以以UTF-8格式存储(CCSID 1208)。这可以很好地处理智能引号和其他“特殊字符”。根据您的数据库和应用程序结构,这样的更改可能实现起来非常小,或者它可能非常大并且有风险!但是实现无损翻译的唯一方法是使用unicode风格,UTF-8是最明智的。

qc6wkl3g

qc6wkl3g2#

有些评论者说你的问题是“主观的”(不是指基于观点的问题,而是指每个人的具体要求与其他人的要求略有不同),或者定义不明确,或者根本不可能解决......这些评论在技术上是正确的。
但是你在寻找一些你可以做的实际的事情来改善这种情况,这也是完全有效的。
在实现难度与结果准确性之间取得平衡的最佳方法是将你已经发现的内容与来自不太负面的评论者的建议结合起来:

  • 使用标准规范化过程处理变音符号和其他“可标准规范化”的字符。
  • 使用您自己的Map处理其他所有内容(可能包括Unicode General_Category属性,但最终可能需要包括您自己亲手挑选的用其他特定字符替换特定字符的Map)。

上面的 * 可能 * 涵盖了“所有”未来的情况,这取决于数据的来源。或者足够接近所有你可以实现它并完成它的情况。如果你想增加一些健壮性,并将在一段时间内保持这个过程,那么你也可以提出一个列表,列出你想在净化后的结果中允许的所有字符。然后设置某种异常或日志记录机制,使您(或您的继任者)能够在出现新的未处理情况时找到它们,然后可以使用这些情况来改进Map的自定义部分。

50few1ms

50few1ms3#

经过一些挖掘,我能够找到基于this answer的解决方案,使用org.apache.lucene.analysis.ASCIIFoldingFilter
我能找到的所有示例都使用了foldToASCII方法的静态版本,如this project中所示:

private static String getFoldedString(String text) {
    char[] textChar = text.toCharArray();
    char[] output = new char[textChar.length * 4];
    int outputPos = ASCIIFoldingFilter.foldToASCII(textChar, 0, output, 0, textChar.length);
    text = new String(output, 0, outputPos);
    return text;
}

然而,该静态方法有一个注解,它说
此API仅供内部使用,在下一版本中可能会以不兼容的方式进行更改。
因此,经过一些反复试验,我得出了这个避免使用静态方法的版本:

public static String getFoldedString(String text) throws IOException {
    String output = "";
    try (Analyzer analyzer = CustomAnalyzer.builder()
              .withTokenizer(KeywordTokenizerFactory.class)
              .addTokenFilter(ASCIIFoldingFilterFactory.class)
              .build()) {
        try (TokenStream ts = analyzer.tokenStream(null, new StringReader(text))) {
            CharTermAttribute charTermAtt = ts.addAttribute(CharTermAttribute.class);
            ts.reset();
            if (ts.incrementToken()) output = charTermAtt.toString();
            ts.end();
        }
    }
    return output;
}

与我提供的答案here类似。
这正是我要找的,并将字符翻译为它们的ASCII 7位等效版本。
然而,通过进一步的研究,我发现,由于我主要处理Windows-1252编码,并且由于jt 400处理ASCII EBCDIC的方式<->,(CCSID 37)转换,如果将ASCII字符串转换为EBCDIC,然后再转换回ACSII,则丢失的字符只有0x800x9f。因此,受lucene's foldToASCII处理方式的启发,我把下面的方法放在一起,只处理这些情况:

public static String replaceInvalidChars(String text) {
    char input[] = text.toCharArray();
    int length = input.length;
    char output[] = new char[length * 6];
    int outputPos = 0;
    for (int pos = 0; pos < length; pos++) {
        final char c = input[pos];
        if (c < '\u0080') {
            output[outputPos++] = c;
        } else {
            switch (c) {
                case '\u20ac':  //€ 0x80
                    output[outputPos++] = 'E';
                    output[outputPos++] = 'U';
                    output[outputPos++] = 'R';
                    break;
                case '\u201a':  //‚ 0x82
                    output[outputPos++] = '\'';
                    break;
                case '\u0192':  //ƒ 0x83
                    output[outputPos++] = 'f';
                    break;
                case '\u201e':  //„ 0x84
                    output[outputPos++] = '"';
                    break;
                case '\u2026':  //… 0x85
                    output[outputPos++] = '.';
                    output[outputPos++] = '.';
                    output[outputPos++] = '.';
                    break;
                case '\u2020':  //† 0x86
                    output[outputPos++] = '?';
                    break;
                case '\u2021':  //‡ 0x87
                    output[outputPos++] = '?';
                    break;
                case '\u02c6':  //ˆ 0x88
                    output[outputPos++] = '^';
                    break;
                case '\u2030':  //‰ 0x89
                    output[outputPos++] = 'p';
                    output[outputPos++] = 'e';
                    output[outputPos++] = 'r';
                    output[outputPos++] = 'm';
                    output[outputPos++] = 'i';
                    output[outputPos++] = 'l';

                    break;
                case '\u0160':  //Š 0x8a
                    output[outputPos++] = 'S';
                    break;
                case '\u2039':  //‹ 0x8b
                    output[outputPos++] = '\'';
                    break;
                case '\u0152':  //Π0x8c
                    output[outputPos++] = 'O';
                    output[outputPos++] = 'E';
                    break;
                case '\u017d':  //Ž 0x8e
                    output[outputPos++] = 'Z';
                    break;
                case '\u2018':  //‘ 0x91
                    output[outputPos++] = '\'';
                    break;
                case '\u2019':  //’ 0x92
                    output[outputPos++] = '\'';
                    break;
                case '\u201c':  //“ 0x93
                    output[outputPos++] = '"';
                    break;
                case '\u201d':  //” 0x94
                    output[outputPos++] = '"';
                    break;
                case '\u2022':  //• 0x95
                    output[outputPos++] = '-';
                    break;
                case '\u2013':  //– 0x96
                    output[outputPos++] = '-';
                    break;
                case '\u2014':  //— 0x97
                    output[outputPos++] = '-';
                    break;
                case '\u02dc':  //˜ 0x98
                    output[outputPos++] = '~';
                    break;
                case '\u2122':  //™ 0x99
                    output[outputPos++] = '(';
                    output[outputPos++] = 'T';
                    output[outputPos++] = 'M';
                    output[outputPos++] = ')';
                    break;
                case '\u0161':  //š 0x9a
                    output[outputPos++] = 's';
                    break;
                case '\u203a':  //› 0x9b
                    output[outputPos++] = '\'';
                    break;
                case '\u0153':  //œ 0x9c
                    output[outputPos++] = 'o';
                    output[outputPos++] = 'e';
                    break;
                case '\u017e':  //ž 0x9e
                    output[outputPos++] = 'z';
                    break;
                case '\u0178':  //Ÿ 0x9f
                    output[outputPos++] = 'Y';
                    break;
                default:
                    output[outputPos++] = c;
                    break;
            }
        }
    }

    return new String(Arrays.copyOf(output, outputPos));
}

由于我的真实的问题是Windows-1252到Latin-1(ISO-8859-1)的转换,因此这里有一个supporting material,它显示了上述方法中使用的Windows-1252到Unicode的转换,以最终获得Latin-1编码。

相关问题