US-ASCII字符串(解)压缩into/from一个字节数组(7bits/character)

US-ASCII string (de-)compression into/from a byte array (7 bits/character)

众所周知,ASCII 使用 7 位来编码字符,因此用于表示文本的字节数总是小于文本字母的长度

例如:

    StringBuilder text = new StringBuilder();
    IntStream.range(0, 160).forEach(x -> text.append("a")); // generate 160 text
    int letters = text.length();
    int bytes = text.toString().getBytes(StandardCharsets.US_ASCII).length;
    System.out.println(letters); // expected  160,  actual 160
    System.out.println(bytes); //   expected  140,  actual 160

总是 letters = bytes,但预期是 letters > bytes

主要问题: in smpp protocol sms body must be <= 140 byte, if we used ascii encoding , 然后你可以写 160 个字母 =(140*8/7), 所以我想用 7-bit based ascii 编码的文本, 我们正在使用 JSMPP

任何人都可以向我解释并指导我正确的方法,在此先感谢(:

根据编码类型,字节长度会有所不同。检查下面的例子。

String text = "0123456789";
byte[] b1 = text.getBytes(StandardCharsets.US_ASCII);
System.out.println(b1.length);
// prints "10"

byte[] utf8 = text.getBytes(StandardCharsets.UTF_8);
System.out.println(utf8.length); 
// prints "10"

byte[] utf16= text.getBytes(StandardCharsets.UTF_16);
System.out.println(utf16.length); 
// prints "22"

byte[] utf32 = text.getBytes(StandardCharsets.ISO_8859_1);
System.out.println(utf32.length); 
// prints "10" 

(160*7-160*8)/8 = 20,因此您预计脚本末尾使用的字节数会减少 20 个。但是,寄存器有一个最小大小,所以即使你不使用所有位,你仍然不能将它连接到另一个值,所以你仍然使用 8 位字节作为你的 ASCII 码,这就是为什么你得到相同的号码。比如小写的"a"在ASCII

中就是97
‭01100001‬

请注意前导零仍然存在,即使它没有被使用。你不能只用它来存储另一个值的一部分。

结论是,在纯 ASCII 中,字母必须始终等于字节数。

(或者想象一下将 7 号物体放入 8 号盒子中。您不能将物体劈成碎片,因此盒子的数量必须等于物体的数量 - 至少在这种情况下。)

没有。在 "modern" 环境中(从 3 或 4 年前开始),ASCII 字符集的 ASCII 字符编码使用 8 位代码单元,然后将其序列化为每个字节。这是因为我们要在 "octets"(8 位字节)中移动和存储数据。这种字符编码恰好总是将高位设置为 0。

可以说,很久以前就有一种用于 ASCII 字符集的 7 位字符编码。即使那样,数据也可能已作为八位字节移动或存储。高位将用于某些 application-specific 目的,例如奇偶校验。某些系统会将其归零以尝试提高互操作性,但最终由于不是“8 位安全”而阻碍了互操作性。有了强大的互联网标准,这样的系统几乎都成为过去。

这是一个没有任何库的快速而肮脏的解决方案,即只有 JRE on-board 手段。它没有针对效率进行优化,并且不检查消息是否确实是 US-ASCII,它只是假设它。这只是一个概念证明:

package de.scrum_master.Whosebug;

import java.util.BitSet;

public class ASCIIConverter {
  public byte[] compress(String message) {
    BitSet bits = new BitSet(message.length() * 7);
    int currentBit = 0;
    for (char character : message.toCharArray()) {
      for (int bitInCharacter = 0; bitInCharacter < 7; bitInCharacter++) {
        if ((character & 1 << bitInCharacter) > 0)
          bits.set(currentBit);
        currentBit++;
      }
    }
    return bits.toByteArray();
  }

  public String decompress(byte[] compressedMessage) {
    BitSet bits = BitSet.valueOf(compressedMessage);
    int numBits = 8 * compressedMessage.length - compressedMessage.length % 7;
    StringBuilder decompressedMessage = new StringBuilder(numBits / 7);
    for (int currentBit = 0; currentBit < numBits; currentBit += 7) {
      char character = (char) bits.get(currentBit, currentBit + 7).toByteArray()[0];
      decompressedMessage.append(character);
    }
    return decompressedMessage.toString();
  }

  public static void main(String[] args) {
    String[] messages = {
      "Hello world!",
      "This is my message.\n\tAnd this is indented!",
      " !\"#$%&'()*+,-./0123456789:;<=>?\n"
        + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_\n"
        + "`abcdefghijklmnopqrstuvwxyz{|}~",
      "1234567890123456789012345678901234567890"
        + "1234567890123456789012345678901234567890"
        + "1234567890123456789012345678901234567890"
        + "1234567890123456789012345678901234567890"
    };

    ASCIIConverter asciiConverter = new ASCIIConverter();
    for (String message : messages) {
      System.out.println(message);
      System.out.println("--------------------------------");
      byte[] compressedMessage = asciiConverter.compress(message);
      System.out.println("Number of ASCII characters = " + message.length());
      System.out.println("Number of compressed bytes = " + compressedMessage.length);
      System.out.println("--------------------------------");
      System.out.println(asciiConverter.decompress(compressedMessage));
      System.out.println("\n");
    }
  }
}

控制台日志如下所示:

Hello world!
--------------------------------
Number of ASCII characters = 12
Number of compressed bytes = 11
--------------------------------
Hello world!


This is my message.
    And this is indented!
--------------------------------
Number of ASCII characters = 42
Number of compressed bytes = 37
--------------------------------
This is my message.
    And this is indented!


 !"#$%&'()*+,-./0123456789:;<=>?
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
`abcdefghijklmnopqrstuvwxyz{|}~
--------------------------------
Number of ASCII characters = 97
Number of compressed bytes = 85
--------------------------------
 !"#$%&'()*+,-./0123456789:;<=>?
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
`abcdefghijklmnopqrstuvwxyz{|}~


1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
--------------------------------
Number of ASCII characters = 160
Number of compressed bytes = 140
--------------------------------
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890