大写和小写字母如何仅相差一位?

时间:2010-08-25 20:16:45

标签: ascii bit-manipulation case-sensitive

我在Behrouza Forouzan撰写的数据和通信网络书中找到了一个关于大写和小写字母的例子,这些字母在7位代码中只有一位不同。

  

例如,字符A是1000001(0x41),字符a是1100001(0x61)。区别在于第6位,大写字母为0,小写字母为1。如果我们知道一种情况的代码,我们可以通过在十进制中加上或减去32来轻松找到另一种情况的代码,或者我们可以只翻转第六位。

这一切意味着什么?

我发现自己对所有这些事情都非常困惑。有人可以举例说明这些事情是如何运作的吗?

7 个答案:

答案 0 :(得分:35)

让我们使用一个你会发现更熟悉的案例:基础10。

  1. 假设我们有一台基础10计算机,其中每个10位存储0到9的值,10byte是5 10位长,因此每个字节可以存储100,000个值(0到99,999)。

  2. 您希望将字母分配给10字节的特定位置,以便此计算机可以与其他计算机进行文本数据通信。你可以做到这一点的一种方式是:

    00101 A    00201 a
    00102 B    00202 b
    00103 C    00203 c
    00104 D    00204 d
    00105 E    00205 e
    00106 F    00206 f
    00107 G    00207 g
    00108 H    00208 h
    00109 I    00209 i
    00110 J    00210 j
    00111 K    00211 k
    00112 L    00212 l
    00113 M    00213 m
    00114 N    00214 n
    00115 O    00215 o
    00116 P    00216 p
    00117 Q    00217 q
    00118 R    00218 r
    00119 S    00219 s
    00120 T    00220 t
    00121 U    00221 u
    00122 V    00222 v
    00123 W    00223 w
    00124 X    00224 x
    00125 Y    00225 y
    00126 Z    00226 z
    
  3. 您是否看到每个小写字母与大写字母不同,只有一个10位数字,在右边的第3列中?它没有以这种方式设计 。它简单方便,因为任何时候我们想要调整一个字母的情况我们可以简单地修改其中一个数字(10位)而不关心数字的其余部分是什么,或者在我们可以做的时候用二十六个不同的转换来打扰一个。我们不能选择第二个数字,因为它们不是相隔100个,而是相距10个并且会重叠。

  4. 现在,在基数2中它完全相同,但不是每个位代表0-9,它只能代表0-1。使用8个2位只给出了256种可能的组合,0-255。二进制大写和小写字母的ASCII代码如下所示:

    01000001 A        01100001 a
    01000010 B        01100010 b
    01000011 C        01100011 c
    01000100 D        01100100 d
    01000101 E        01100101 e
    01000110 F        01100110 f
    01000111 G        01100111 g
    01001000 H        01101000 h
    01001001 I        01101001 i
    01001010 J        01101010 j
    01001011 K        01101011 k
    01001100 L        01101100 l
    01001101 M        01101101 m
    01001110 N        01101110 n
    01001111 O        01101111 o
    01010000 P        01110000 p
    01010001 Q        01110001 q
    01010010 R        01110010 r
    01010011 S        01110011 s
    01010100 T        01110100 t
    01010101 U        01110101 u
    01010110 V        01110110 v
    01010111 W        01110111 w
    01011000 X        01111000 x
    01011001 Y        01111001 y
    01011010 Z        01111010 z
    

    与以前一样,它们只有一个2位数字,在右边的第6列中。我们不能使用任何更靠右边的数字(更小)因为那时列表会重叠(2 ^ 5 = 32,因此我们使用所有位0到5,但是2 ^ 4 = 16,这不能覆盖字母表中的26个字母)。

  5. 只是为了填写一些内容,这里是这些二进制值的含义示例。让我们拿一个用于G.要理解01000111在二进制中的含义:

     Pos:   7  6  5  4  3  2  1  0
     Bit:   0  1  0  0  0  1  1  1
     Val: 128 64 32 16  8  4  2  1
    Mult:   0 64  0  0  0  4  2  1
     Add: 64 + 4 + 2 + 1 = 71, which is the ASCII code for G.
    

    对我在上面构建的特殊基础10系统中的字母G执行相同的操作:

      Pos:     4    3    2    1    0
    10Bit:     0    0    1    0    7
      Val: 10000 1000  100   10    1
     Mult:     0    0  100    0    7
      Add: 100 + 7 = 107, which is my special 10ASCII code for G.
    

    回顾二进制的“Val”行。你是否看到从右边开始,每个值是前一个的两倍?每次我们得到1,2,4,8,16,32,64,128,256,512等两倍。这就是二进制数字的位置如何确定其值,就像十进制数字的位置确定其值为10:1,10,100,1000,10000,100000等等。

    我意识到这看起来很愚蠢,因为我所做的只是将107转换为107 ......但107不仅仅是一个数字,它是一种简写形式:

    1 hundreds + 0 tens + 7 ones.
    

    我们可以代表的另一种方式是

    0 x 10^4 + 0 x 10^3 + 1 x 10^2 + 0 x 10^1 + 7 x 10^0.
    

    同样,01000111不仅仅是二进制数,它是

    的简写形式
    0 x 2^7 + 1 x 2^6 + 0 x 2^5 + 0 x 2^4 + 0 x 2^3 + 1 x 2^2 + 1 x 2^1 + 1 x 2^0
    

    我已经向您展示了这一点:

    0 + 64 + 0 + 0 + 0 + 4 + 2 + 1
    = 64 + 4 + 2 + 1
    = 71
    
  6. 此外,您可能想知道0x410x61的含义。 0x部分表示要跟随的数字应理解为十六进制,即16。我们的数字系统中只有10位数字,因此我们需要6位数字。因此,十六进制使用数字0-9并将字母AF视为剩余数字,其中A是10直到F为15.十六进制对于计算机非常方便,因为16是2的幂,因此是8位字节正好采用两个十六进制数字进行编码(每个十六进制数字正好编码四个二进制数字)。结束0x41,将4扩展为二进制代表0100并将1扩展为二进制代表0001,即可获得01000001,您可以看到是A的代码,如图所示。要将它转换为十进制,它是4 x 16 + 1 x 1 = 65.我们将4乘以16,因为每个连续的十六进制数字向左是前一个数字的16倍,遵循与我在上面显示的基数2和10相同的模式

    我希望这足以让您更多地了解二进制和ASCII代码。

    注1:你可能认为,一个字节而不是2位的8位的原因是,在计算的早期,人们认为8是一个更有用的数字位,作为2位“字节”只能编码4个值。要传输字母表的大写和小写字母,需要3个字节!二进制中没有固有的东西强制每字节选择8位,除了8也是2的幂,这使得处理二进制信息的许多数学更简单,并且事物在边缘上更好地对齐。如果他们每个字节选择了6位,我相信事情会很糟糕,并且不会充分利用可用的全部值。

    注2:我的10字节中的5位系统是基于每字节使用10个10位的不切实际,这会产生非常大的数量,会浪费大量的存储空间。我选择了五个,因为十个可以被它整除,这无疑是有用的。 (最初,我的答案每10字节使用十个10比特,但它太大了!)

答案 1 :(得分:3)

大写和小写字母之间的这种关系是故意的。当制定ASCII码时,计算机硬件是原始的,需要软件来保存每个字节。翻转单个位只需要很少的硬件或代码即可完成。

答案 2 :(得分:2)

要添加或减去32,首先必须知道该字符是大于还是小于'A'。

编写本书时,大多数人使用的编程语言没有字符串或.equalsIgnoreCase。这是pre-i18n,当一个企业有一个服务器时,你会telnet到它(如xterm),并得到一个命令行菜单。他所描述的内容通常用于为用户创建一个不错的不区分大小写的菜单,利用ascii表的数字布局。

它可以非常快,因为无论字符是已经大写还是小写,都有逐位汇编指令在任一方向上进行数学运算。

c = c | 32 //到大写

c = c& (1 + 2 + 4 + 8 + 16 + 0 + 64 + 128)//到小写

假设您有类似Java的语言,没有对象或标准库。 您的网络作者正在提示您编写如下代码:

    public static void main()
    {
        println("What would you like to do?");
        println("Inventory (inv)");
        println("Reports (rep)");

        char[] ca = readUserInput();        
        for (int i = 0; i < ca.length; i++)
            ca[i] = ca[i] | 32;  // convert to uppercase, by ensuring bit 32 is set

        if (compareInput(ca, "INV") == true)
            doInventory();
    }

您是否尝试过搜索Google,有时会将一个人的名字大写?

答案 3 :(得分:1)

看看,第6位= 32,所以如果你翻转它,你可以减去或添加32

Bit value
1   1
2   2
3   4
4   8
5   16
6   32 (32 = hex 20)

现在,如果你看这里http://asciitable.com/,你可以看到所有字符的ascii表,并注意到A = 65和a = 97

答案 4 :(得分:1)

http://asciitable.com/

0x61 is hexadecimal for 97 = a
0x41 is hexadecimal for 65 = A

因此,减去/添加小数32确实是转换为大写/小写的方式。

Z is 90 = 0b1111010    = 0x5A
z is 122 = 0b1011010   = 0x7A

这是二进制中0b01000000或十进制中的0x20或32的差异。

因此切换第6位会改变大小写。

答案 5 :(得分:1)

我认为这些答案中的大多数都是不必要的复杂而且偶尔也会居高临下。

ascii字符映射的十进制是任意的,与理解base 2或base 10的工作原理并没有任何关系。这纯粹是一种方便的事情。如果有人错误地编写了一个小写字符,但意味着大写字母,那么只需翻转一个位而不必重新编码整个字节就更方便了。只需翻转一下就不容易出现人为错误。如果输出是'a'但我们想要'A',至少我们知道我们得到的大部分位正确,我们只需要翻转2 ^ 5来加或减32.这很容易。为什么要特别选择第5位(有些人说不是6,你从0开始......),很明显,只有一位翻转才能满足26个字符的两个范围。如果你在一个价值较低的位上做到这一点,你必须翻转多个。

答案 6 :(得分:0)

template<char TLBound, char TUBound>
struct CharRange
{
    enum 
    {
        LBound = TLBound,
        UBound = TUBound
    };

    static bool InRange(char ch)
    {
        return (ch >= LBound)  && (ch <= UBound);
    };
};

typedef CharRange<'a', 'z'> lcaseLetters;
typedef CharRange<'A', 'Z'> ucaseLetters;

char toUpper(char ch)
{
    if(lcaseLetters::InRange(ch))
    {
        return (ch ^ (0x1 << 5));
    }

    return ch;
}

char toLower(char ch)
{
    if(ucaseLetters::InRange(ch))
    {
        return (ch ^ (0x1 << 5));
    }

    return ch;
}