如何在Java中将字节数组转换为字符串时处理负字节值?

huangapple go评论86阅读模式
英文:

How to handle negative byte values when converting byte array to string in Java?

问题

我在Java中使用ThermalPrinter打印食谱,但遇到了一个问题。这个打印机只接受文本进行打印,因此我需要将所有命令发送为字符串。大多数命令都只是一些ASCII命令的序列。
因此,我将要打印的所有文本和需要执行的所有命令都收集到一个字符串中,然后将其发送到打印机。我像这样将命令添加到我的字符串中:

final byte[] bytes = {0x1B, 0x25, 0x18};
printString += new String(bytes);

这个方法可以正常工作,除了一个情况。在最后的切纸命令中,其中一个值是0xF0。由于Java将字节解释为有符号值,这会转换为-16。如果我将其添加到字符串中,它将不会添加我想要的字节。它会添加3个字节,这是-16的文本表示,但它应该只添加一个字节。

如果我运行以下代码:

final byte[] bytes = {0x1B, (byte) 0xF0 , 0x06, 0x01, 0x01};
String newString = new String(bytes);

for (byte b : bytes) {
    System.out.print(b + " ");
}
System.out.println();

for (byte b : newString.getBytes()) {
    System.out.print(b + " ");
}
System.out.println();

它会给我以下输出:

27 -16 6 1 1 
27 -17 -65 -67 6 1 1 

字节数组包含了正确的值,但字符串没有。我期望这两个输出是相同的。这是String类的一个限制吗?还是有其他方法可以解决这个问题?

更多信息:打印机是Hengstler eXtendo X-56。为了与打印机通信,我使用制造商提供的API。它是一个我添加到我的系统(debian)并通过JNA加载的C库。我用于打印数据的API头文件中的方法如下:

int exo_api_printer_write(intptr_t printer, unsigned char * data, int size, unsigned long timeout_ms)

JNA接口函数如下:

int exo_api_printer_write(Pointer printer, String data, int size, long timeout_ms);

因此,我必须在某个时候将我的字节转换为字符串以发送到打印机。现在我的问题是切纸的打印机命令:0x1B、0xF0、0x06、0x01、0x01,无法将其转换为字符串。

英文:

I am using a ThermalPrinter for recipes in java, or i try to. This printer accepts only Text for printing, so i need to send all commands as strings. Most are just some sequence of the asci commands.
So i collect all the text i want to print and all commands that need to be done in one String and then send this to the printer. I add the commands to my string like this:

final byte[] bytes = {0x1B, 0x25, 0x18};
printString += new String(bytes);

This works fine except for on case. In the final Command to cut the paper on of the values is 0xF0. this works out to -16 since java interprets byte as signed value. If then add this to the string it adds not the bytes i want. It adds 3 bytes instead which will be for the -16 as text. But it should just add the one byte.

If i run this:

        final byte[] bytes = {0x1B, (byte) 0xF0 , 0x06, 0x01, 0x01};
        String newString = new String(bytes);

        for (byte b : bytes) {
            System.out.print(b + " ");
        }
        System.out.println();

        for (byte b : newString.getBytes()) {
            System.out.print(b + " ");
        }
        System.out.println();

It gives me:

27 -16 6 1 1 
27 -17 -65 -67 6 1 1 

The byte array contains the correct value but not the string. I would expect the two outputs to be the same. Is this a limitation of the String class? Or is there a way to do this?

Some more information. The printer is a Hengstler eXtendo X-56.
To communicate to the printer i use the API supplied by the Manufacturer. Its a C lib i add to my system(debian) and then load via JNA. The Method from api header i use to print data.

int exo_api_printer_write(intptr_t printer, unsigned char * data, int size, unsigned long timeout_ms)

The JNA interface funtion looks like:

int exo_api_printer_write(Pointer printer, String data, int size, long timeout_ms);

So at some point i Have to convert my bytes into a String to send it to the printer. My Problem now is the printer command to cute the paper. The command is: 0x1B, 0xF0, 0x06, 0x01, 0x01 and cannot convert this to a string.

答案1

得分: 1

我找到了一个解决方案。实际上,这是从这里的答案中组合而来的。

然而,对我来说,解决方案是修改JNA接口。由于打印方法需要一个char*,所以我想到可以使用字符串。但是在Java这一侧,我必须使用一个byte[]。现在我可以按自己的方式发送所有数据。
这样,我就不需要以任何方式使用字符串。我想我的问题是我在不应该使用字符串的地方使用了它。

如果有人在某个时候需要它,这里是我现在使用的完整的JNA接口(您需要将Hengstler API安装到系统中):

public interface ExoAPI extends Library {
    ExoAPI INSTANCE = (ExoAPI) Native.load("ExoApi", ExoAPI.class);

    String exo_api_get_library_info();
    long exo_api_printer_open(String port);
    int exo_api_printer_set_serial(long printer, long baud_rate, char data_bits, char parity, char stop_bits);
    void exo_api_printer_close(long printer);
    int exo_api_printer_write(long printer, byte[] data, int size, long timeout_ms);
    int exo_api_printer_read(long printer, byte[] data, int size, long timeout_ms);
    int exo_api_printer_clear(long printer);
}
英文:

I figured out a solution. It is actually a combination from the answers here.

The solution for me, however, was to alter the JNA interface. Since the print method expects a char* i just thought great i will use a string. But i had to use a byte[] on the java side here. Now i can send all the data as i want to.
This way i don`t need to use a string in any way. I guess my problem then, was that i used a string for something it is not intended for.

In case someone need it at some time, here the full JNA interface as i use it now(You need to intstall the Hengstler API to your system):

public interface ExoAPI extends Library {
ExoAPI INSTANCE = (ExoAPI) Native.load("ExoApi", ExoAPI.class);

String exo_api_get_library_info();
long exo_api_printer_open(String port);
int exo_api_printer_set_serial(long printer, long baud_rate, char data_bits, char parity, char stop_bits);
void exo_api_printer_close(long printer);
int exo_api_printer_write(long printer, byte[] data, int size, long timeout_ms);
int exo_api_printer_read(long printer, byte[] data, int size, long timeout_ms);
int exo_api_printer_clear(long printer);
}

答案2

得分: 0

如果您使用_ISO/IEC 8859-1进行编码,它似乎会保留-16_字节。

byte[] bytes = { 0x1b, (byte) 0xf0, 0x06, 0x01, 0x01 };

String stringA = new String(bytes, StandardCharsets.ISO_8859_1);
String stringB = new String(bytes, StandardCharsets.UTF_8);
String stringC = new String(bytes, StandardCharsets.US_ASCII);

System.out.println(Arrays.toString(stringA.getBytes(StandardCharsets.ISO_8859_1)));
System.out.println(Arrays.toString(stringB.getBytes(StandardCharsets.UTF_8)));
System.out.println(Arrays.toString(stringC.getBytes(StandardCharsets.US_ASCII)));

输出

[27, -16, 6, 1, 1]
[27, -17, -65, -67, 6, 1, 1]
[27, 63, 6, 1, 1]
英文:

If you use ISO/IEC 8859-1 for encoding, it appears to preserve the -16 byte.

byte[] bytes = { 0x1b, (byte) 0xf0, 0x06, 0x01, 0x01 };

String stringA = new String(bytes, StandardCharsets.ISO_8859_1);
String stringB = new String(bytes, StandardCharsets.UTF_8);
String stringC = new String(bytes, StandardCharsets.US_ASCII);

System.out.println(Arrays.toString(stringA.getBytes(StandardCharsets.ISO_8859_1)));
System.out.println(Arrays.toString(stringB.getBytes(StandardCharsets.UTF_8)));
System.out.println(Arrays.toString(stringC.getBytes(StandardCharsets.US_ASCII)));

Output

[27, -16, 6, 1, 1]
[27, -17, -65, -67, 6, 1, 1]
[27, 63, 6, 1, 1]

答案3

得分: 0

当在字符串和字节数组之间转换时,您正在使用某种编码机制。如果您不指定它,它将使用您平台的默认编码。在您的情况下,似乎是UTF-8,因为某些字符被编码为3个字节。

使用ISO-8859-1编码,每个字符被编码为一个字节。尝试改用它:

...
import static java.nio.charset.StandardCharsets.ISO_8859_1;
...

final byte[] bytes = {0x1B, (byte) 0xF0 , 0x06, 0x01, 0x01};
String newString = new String(bytes, ISO_8859_1);

for (byte b : bytes) {
    System.out.print(b + " ");
}
System.out.println();

for (byte b : newString.getBytes(ISO_8859_1)) {
    System.out.print(b + " ");
}
System.out.println();

请注意,这段代码中的特殊字符(")是HTML实体,用于表示双引号。您在代码中可能需要使用正常的双引号(")。

英文:

When converting between strings and byte arrays, you are using some encoding mechanism. If you don't specify it, it will use the default encoding for your platform. In your case, it seems to be UTF-8 because some characters are encoded on 3 bytes.
With the ISO-8859-1 encoding, each character is encoded as a single byte. Try using it instead:

...
import static java.nio.charset.StandardCharsets.ISO_8859_1;
...

    final byte[] bytes = {0x1B, (byte) 0xF0 , 0x06, 0x01, 0x01};
    String newString = new String(bytes, ISO_8859_1);

    for (byte b : bytes) {
        System.out.print(b + " ");
    }
    System.out.println();

    for (byte b : newString.getBytes(ISO_8859_1)) {
        System.out.print(b + " ");
    }
    System.out.println();

答案4

得分: 0

由于您正在使用的API期望一个char[],而您似乎想要发送原始数据,只需创建一个Java char[],从中创建一个String,然后使用该字符串进行调用:

final char[] chars={0x1B, 0xF0, 0x06, 0x01, 0x01};
String newString = String.valueOf(chars);
//将newString发送给打印机

//一些测试
for(char c:chars){
    System.out.print((int)c);
    System.out.print(' ');
}
byte[] bytes = newString.getBytes();
for(byte b:bytes){
    System.out.print(b);
    System.out.print(' ');
}
英文:

Since the API you are working with expects a char[] and you seem to want to send raw data there, just create a Java char[], create a String from that and then call it with that:

final char[] chars={0x1B, 0xF0, 0x06, 0x01, 0x01};
String newString = String.valueOf(chars);
//send newString to the printer

//some tests
for(char c:chars){
    System.out.print((int)c);
    System.out.print(' ');
}
byte[] bytes = newString.getBytes();
for(byte b:bytes){
    System.out.print(b);
    System.out.print(' ');
}

huangapple
  • 本文由 发表于 2023年6月2日 06:07:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386026.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定