如何使用Java翻译字符串?

3
我需要一个翻译程序,可以高效地将任何字符转换为其他字符或一组字符。显而易见的方法是使用输入字符串中字符的值作为索引来访问256个条目的翻译数组。
给定一个初始数组,每个条目都设置为其值,例如十六进制'37'将出现在第56个条目中(允许00为第一个),然后用户就可以替换所需的任何字符。
例如1:我想将字符串映射为“A”表示字母字符,“N”表示数字字符,“B”表示空格字符,“X”表示其他任何字符。因此,“SL5 3QW”变成“AANBNAA”。
例如2:我想将一些字符进行转换,例如“œ”(x'9D')转换为“oe”(x'6F65'),“ß”转换为“ss”,“å”转换为“a”等。
如何从输入字符串中的字符获取数值以将其用作索引访问翻译数组?
在Excel中使用函数CODE很容易,在IBM汇编语言中也很简单,但我找不到Java中的方法。

1
你意识到你有可能把"höra"(听)误写成"hora"(妓女)了吗? - Christoffer Hammarström
5个回答

5

虽然这有点离题,但如果您想全面地进行字符翻译,则不能仅使用String.charAt(int)。在Java字符串中,大于65535的Unicode代码点表示为两个连续的char值。

处理这个问题的正确方法是使用String.codepointAt(int)来提取每个代码点,并使用String.offsetByCodePoints(int, int)来遍历代码点位置。


3

2
看到那么多理解Unicode的人仍然主张使用charAt(),真是让人不寒而栗——如果超出BMP范围的字符成为主流使用,大多数Java代码都会以微妙的方式崩溃;感觉就像1995年重演,当时你通过电子邮件发送非ASCII字符并在英语软件中正确显示的机会只有10%左右。 - Michael Borgwardt
1
@Michael:没错,这个API已经过时了。就算不被API误导,理解这些东西也很困难。 - Christoffer Hammarström

2

HashMap<String, String>应该能够很好地工作。没有必要在这样一个简单的问题上过度设计。


+1 - 这很容易定义,不需要硬编码,并且可以处理稀疏的翻译...当然,这会增加堆的开销。 - kdgregory
是的,它既不是最快的解决方案,也不是最小的解决方案(在内存方面),但对于所有标准情况,它都能表现出色,并且非常容易更改和维护。仅这一点就足以使用它,直到它成为瓶颈,然后才应该寻找更复杂的解决方案。老实说,它应该成为您的基准。 - Kajetan Abt

1

回答这个问题有不同的方法。最简单的方法可能是针对每个问题分别提出答案:


问题1:

例如:我想将字符串映射为"A",对于字母字符为"A",数字字符为"N",空格字符为"B",其他任何字符为"X"。因此,"SL5 3QW"变成了"AANBNAA"。

简单解决方案:

public static String map(final String input){
    final char[] out = new char[input.length()];
    for(int i = 0; i < input.length(); i++){
        final char c = input.charAt(i);
        final char t;
        if(Character.isDigit(c)){
            t = 'N';
        } else if(Character.isWhitespace(c)){
            t = 'B';
        } else if(Character.isLetter(c)){
            t = 'A';
        } else{
            t = 'X';
        }
        out[i] = t;
    }
    return new String(out);
}

测试:

public static void main(final String[] args){
    System.out.println(map("SL5 3QW"));
}

输出:

AANBNAA


问题2:

例如2. 我想将一些字符进行翻译,例如将“œ”(x'9D')翻译为“oe”(x'6F65'),将“ß”翻译为“ss”,将“å”翻译为“a”等。

解决方案:

这是标准功能,您应该使用Normalizer API来实现。请参考这些 以前的答案。


大局观

但是仔细想想,当然有一个更一般的解决方案来解决您的问题。让我们看看那些喜欢if/else的人会给我多少踩。定义一个转换器接口,接受特定字符和/或字符类,并将它们映射到其他字符:

public interface CharTransformer{
    boolean supports(char input);
    char transform(char input);
}

现在定义一个方法,您可以使用字符串和此类转换器的集合调用该方法。对于每个单个字符,将查询每个转换器以查看它是否支持此字符。如果支持,则让它执行转换。如果没有找到字符的转换器,则抛出异常。
public static String mapWithTransformers(final String input,
    final Collection<? extends CharTransformer> transformers){
    final char[] out = new char[input.length()];
    for(int i = 0; i < input.length(); i++){
        final char c = input.charAt(i);
        char t = 0;
        boolean matched = false;
        for(final CharTransformer tr : transformers){
            if(tr.supports(c)){
                matched = true;
                t = tr.transform(c);
                break;
            }
        }
        if(!matched){
            throw new IllegalArgumentException("Found no Transformer for char: "
                + c);
        }
        out[i] = t;
    }
    return new String(out);
}

还有一件事:地图

注意:其他人建议使用地图。虽然我认为标准地图不适合这个任务,但你可以使用Guava的MapMaker.makeComputingMap(function)根据需要计算替换(并自动缓存它们)。这样你就有了一个惰性初始化缓存映射。


精彩!谢谢。有很多东西可以尝试。我显然有很多需要学习的地方! - Steve

1

正如Christoffer所说,使用Unicode字符,256个元素的数组是不够的。

一种方法是使用HashMap<Character,String>将每个字符映射到所需的翻译值,并使用String.charAt()依次提取每个字符。您还可以查看Character类上的一些方法,例如isDigit()isLetter()来完成一些工作;这可能比为每个“字母”(在多种语言中)构建映射更容易。

通过使用HashMap,您只需要为要翻译的字符定义映射。对于没有映射的字符(哈希映射返回null),您可以指定默认值或将它们保持不变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接