【问题标题】:Calculating character values in Java given an initial character and an offset给定初始字符和偏移量,在 Java 中计算字符值
【发布时间】:2014-03-06 09:29:21
【问题描述】:

我想生成单个字符串以用作默认标签——想想电子表格应用程序中的列标签。在我的特殊情况下,我只需要从 ["A".."Z"] 中的字符串集合中提取,但我尝试的解决方案可以应用于小写 Latin1 字母、数字、希腊字母表中的字符等。

Java 中的一个常见解决方案是这样的:

static final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

static public char getLabel( int index )
{
    return alphabet.charAt( index );
}

它相当有效,如果index 不在 [0..25] 中,则会导致运行时错误,但它是典型的 Java,因为它很冗长,需要更多代码来验证它是否正常工作 - - 忘记了“U”还是用“V”转置了等等...

所以,相反,我查看了Character 类,看它是否提供了检索序数值(或 Unicode 索引或数值)的方法,这些值可以为诸如 'A' 之类的起始字母返回然后可以将 Unicode 块和类别添加到整数中,生成用于“查找”所需字符的结果,该结果使用另一种方法返回 char,给定有效的整数值,其中“有效”取决于 Unicode 编码。果然,有一些方法可以做到这一点,而且还有更多。事实上,似乎有几种方法可以做同样的事情,其中​​一些方法可以通过额外的选项来做同样的事情,例如为数值指定一个基数,然后在尝试理解“代码点”之间的差异时花费大量时间。 “数值”、“数字”(比方法名称所暗示的更复杂)等。简而言之,Character 似乎为我的简单要求提供了有用的方法,但包装在一个复杂得多的包中想清楚我需要做什么。

最后,可以选择对char 原语执行算术运算。比如:

assert 'B' == 'A' + 1;

嗯,差不多。 Java 通过在计算结果之前将所有小于int 的“整数”类型转换为int 来使事情复杂化。因为char 被认为是整数类型——并且是语言中唯一的无符号整数——所以即使Character 不是Number 和其他问题,它也会进行相同的转换。尽管如此,在必要时进行一些边界检查和强制转换,'A' + x 似乎非常方便,尽管它存在以下示例中指出的问题:

class CharTest
{
    static final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    static public char getLabel( int index )
    {
        return alphabet.charAt( index );
    }

    public static void main( String[] argv )
    {
        System.out.print( "getLabel( 5 ): " );
        System.out.println( getLabel( 5 ) );

        char a = 'A';
        char b = 'B';

        System.out.print( "a: " );
        System.out.println( a );
        System.out.print( "b: " );
        System.out.println( b );
        System.out.print( "++a: " );
        System.out.println( ++a );
        System.out.print( "--a: " );
        System.out.println( --a );
        System.out.print( "a++: " );
        System.out.println( a++ );
        System.out.print( "a--: " );
        System.out.println( a-- );
        System.out.print( "a += 1: " );
        System.out.println( a += 1 );
        System.out.print( "a -= 1: " );
        System.out.println( a -= 1 );
        System.out.print( "a += 5: " );
        System.out.println( a += 5 );
        System.out.print( "a -= 5: " );
        System.out.println( a -= 5 );

        System.out.print( "a + 1: " );
        System.out.println( a + 1 );
        System.out.print( "a - 1: " );
        System.out.println( a - 1 );

        System.out.print( "a + (char) 1: " );
        System.out.println( a + (char) 1 );
        System.out.print( "a - (char) 1: " );
        System.out.println( a - (char) 1 );

        System.out.print( "a + b: " );
        System.out.println( a + b );

        // The casts are just to show intent. I am aware Java will
        // simply add two ints and silently downcast to short.

        short z = (short) 1 + (short) 1;

        System.out.print( "short z = (short) 1 + (short) 1: " );
        System.out.println( z );

        // The same, only different...except the compiler
        // now requires the cast on the right-hand side
        // of the assignment to z or it fails with the
        // following error message:
        //
        //     error: possible loss of precision

        short x = 1;
        short y = 1;

        z = (short) ( x + y );

        System.out.print( "z = (short) ( x + y ): " );
        System.out.println( z );

        // Demonstrate that a is still 'A'. The following tests
        // produce results which indicate it is NUL ('\0') or
        // something even stranger...

        System.out.print( "a: " );
        System.out.println( a );

        // The following will not compile without the explicit
        // casts on the right-hand side of the assignments.
        // This results from converting a, b, and 5 to ints
        // before adding them which produces an int. The error
        // reported by the compiler is:
        //
        //     error: possible loss of precision

        char c = (char) ( a + 5 );

        System.out.print( "char c = (char) ( a + 5 ): " );
        System.out.println( c );

        char d = (char) ( a + b - a );

        System.out.print( "char d = (char) ( a + b - a ): " );
        System.out.println( d );
    }
}

示例代码产生:

getLabel( 5 ): F
a: A
b: B
++a: B
--a: A
a++: A
a--: B
a += 1: B
a -= 1: A
a += 5: F
a -= 5: A
a + 1: 66
a - 1: 64
a + (char) 1: 66
a - (char) 1: 64
a + b: 131
short z = (short) 1 + (short) 1: 2
z = (short) ( x + y ): 2
a: A
char c = (char) ( a + 5 ): F
char d = (char) ( a + b - a ): B

请注意,涉及使用二元运算符的chars 的表达式需要显式转换,而仅使用一元、值更新运算符的表达式则不需要。

是否有任何理由不使用char 算术来解决我当前的相当琐碎的问题,其中涉及查找已知在Unicode 中特定块内排序的字符?或者,我应该使用索引查找字符串常量、Character 类提供的方法,还是我在 JDK 中忽略的另一个更简单的类?

【问题讨论】:

  • 为什么不直接使用.charAt(index % 26)
  • .charAt(index % 26) 很好,但我认为您的意思是:"ABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(index % 26),对吧?由于应用 % 并不能完全满足我当前的要求,但在其他情况下可以。

标签: java math unicode char character


【解决方案1】:

AFAIK,你没有忽略任何事情。我认为您已经涵盖了针对您的特定用例的所有合理方法。

选择归结为:

  • 您是否想要/需要验证index 参数,以及

  • 您是否希望(在某些时候)能够概括此功能;即处理更大的非连续字符范围。

在您的位置上,我只是选择更简单的方法,将功能包装为辅助方法,然后转向更重要的问题。 (如果您需要重新考虑您的决定,将其作为辅助方法可以简化事情。)


我不确定您想要通过 50 条左右的线路测试程序实现什么目标,或者您为什么要向我们展示它。它似乎只是显示 Java 算术运算符表现正常:-)


FWIW,我认为您在 Character 类等中找不到任何聪明的方法来做这种事情的原因是它们是不必要的。使用标准运算符对字符串(或char[])或算术进行索引涵盖了所有典型用例。

【讨论】:

  • 测试程序现在看起来更清晰了,但它是在试图弄清楚哪些操作员在 char 上工作以及如何工作时构建的。最后,行为本质上与shortbyte相同,但在处理IDE投诉、编译器错误、互联网上的代码片段以及Java的事实时,导致代码爆炸的细节并不清楚7 编译器“理解”short z = 1 + 1 而不需要强制转换(毫无疑问,因为常量折叠发生在编译赋值之前),但不是 short a = 1; short b = 1; short c = a + b; 除非应用强制转换。
  • 多年前,编写 C 代码操作 US-ASCII 字符,这是显而易见且常用的基本算术运算符。我多年来使用的大多数其他语言都提供了类似int ord(char)char tochar(int) 的东西,而不是char 类型的算术。有了 Unicode 和其中的许多“未使用”块,我一直在寻找更“封装”的东西。 Character 基本上提供了包装在 Rube Goldberg 式包装中的内容。无论如何,我终于计算出了足够多的 char 算术细节,可以在这个应用程序中正确使用它。
  • 你好像是一个Java初学者。 byteshortchar 的算术有点不直观,但它都遵循一个简单的规则。也就是说,算术运算符总是产生intlong。 operation-and-becomes 运算符然后隐式执行类型转换,但对于其余部分,您需要显式执行转换......如果您希望将结果解释为 byteshortchar。这不是某些 Java 编译器或某些平台的产物。它是 JLS 中指定的语言的基本部分。所有符合标准的 Java 实现都以这种方式运行。
  • 不是初学者,但我从不打扰 byte 或 short 只是因为 Java 从来没有对它们进行操作,除了转换它们。因此,只有在分配大数组来保存小值时,它们才真正值得考虑。 char 当然是一样的。但是 Java 不考虑 CharacterNumberchar 可以用作一个小的但未签名的 int。经常出现毫无意义的不一致、非常冗长的语法、算术二元运算符是一种以另一个数字作为参数的数字的方法的不对称 OOP 错觉,以及对约定的严重依赖...
  • ...语言(Java bean 和类似 Java bean 的对象,有人知道吗?),以及由明确的 32 位虚拟机提供的跨平台可移植性,Java 比它需要的更难使用当程序员进入该领域不常访问的部分时。
猜你喜欢
  • 2016-07-06
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-11
  • 2017-05-10
  • 1970-01-01
相关资源
最近更新 更多