java按字节截取字符串
2011-01-16 12:33
323 查看
java面试题:
编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输入"我ABC汉DEF",6,应该输出为"我ABC"而不是"我ABC+汉的半个"。
目前很多流行的语言,如C#、Java内部采用的都是 Unicode 16(UCS2)编码,在这种编码中所有的字符都是两个字符,因此,如果要截取的字符串是中、英文、数字混合的,就会产生问题,如下面的字符串:
String s = "a加b等于c,如果a等1、b等于2,那么c等3";
上面的字符串既有汉字,又有英文字符和数字。如果要截取前6个字节的字符,应该是”a加b等",但如果用substring方法截取前6个字符就成了"a 加b等于c"。产生这个问题的原因是将substring方法将双字节的汉字当成一个字节的字符(UCS2字符)处理了。
英文字母和中文汉字在不同的编码格式下,所占用的字节数也是不同的,我们可以通过下面的例子来看看在一些常见的编码格式下,一个英文字母和一个中文汉字分别占用多少字节。
Java代码
import java.io.UnsupportedEncodingException;
public class EncodeTest {
/**
* 打印字符串在指定编码下的字节数和编码名称到控制台
*
* @param s
* 字符串
* @param encodingName
* 编码格式
*/
public static void printByteLength(String s, String encodingName) {
System.out.print("字节数:");
try {
System.out.print(s.getBytes(encodingName).length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(";编码:" + encodingName);
}
public static void main(String[] args) {
String en = "A";
String ch = "人";
// 计算一个英文字母在各种编码下的字节数
System.out.println("英文字母:" + en);
EncodeTest.printByteLength(en, "GB2312");
EncodeTest.printByteLength(en, "GBK");
EncodeTest.printByteLength(en, "GB18030");
EncodeTest.printByteLength(en, "ISO-8859-1");
EncodeTest.printByteLength(en, "UTF-8");
EncodeTest.printByteLength(en, "UTF-16");
EncodeTest.printByteLength(en, "UTF-16BE");
EncodeTest.printByteLength(en, "UTF-16LE");
System.out.println();
// 计算一个中文汉字在各种编码下的字节数
System.out.println("中文汉字:" + ch);
EncodeTest.printByteLength(ch, "GB2312");
EncodeTest.printByteLength(ch, "GBK");
EncodeTest.printByteLength(ch, "GB18030");
EncodeTest.printByteLength(ch, "ISO-8859-1");
EncodeTest.printByteLength(ch, "UTF-8");
EncodeTest.printByteLength(ch, "UTF-16");
EncodeTest.printByteLength(ch, "UTF-16BE");
EncodeTest.printByteLength(ch, "UTF-16LE");
}
}
运行结果如下:
英文字母:A
字节数:1;编码:GB2312
字节数:1;编码:GBK
字节数:1;编码:GB18030
字节数:1;编码:ISO-8859-1
字节数:1;编码:UTF-8
字节数:4;编码:UTF-16
字节数:2;编码:UTF-16BE
字节数:2;编码:UTF-16LE
中文汉字:人
字节数:2;编码:GB2312
字节数:2;编码:GBK
字节数:2;编码:GB18030
字节数:1;编码:ISO-8859-1
字节数:3;编码:UTF-8
字节数:4;编码:UTF-16
字节数:2;编码:UTF-16BE
字节数:2;编码:UTF-16LE
UTF-16BE和UTF-16LE是UNICODE编码家族的两个成员。UNICODE标准定义了UTF-8、UTF-16、UTF-32三种编码格式,共有UTF-8、UTF-16、UTF-16BE、UTF-16LE、UTF-32、UTF-32BE、UTF-32LE七种编码方案。JAVA所采用的编码方案是UTF-16BE。从上例的运行结果中我们可以看出,GB2312、GBK、GB18030三种编码格式都可以满足题目的要求。下面我们就以GBK编码为例来进行解答。
我们不能直接使用String类的substring(int beginIndex, int endIndex)方法,因为它是按字符截取的。'我'和'Z'都被作为一个字符来看待,length都是1。实际上我们只要能区分开中文汉字和英文字母,这个问题就迎刃而解了,而它们的区别就是,中文汉字是两个字节,英文字母是一个字节。
以下是转载自http://www.javaeye.com/topic/216577的代码
不知道是平台原因还是什么,我运行以上程序得不到正确结果:
下面是我修改后的代码,在我的机器上能正确运行:
运行结果结果同上。但还有一个疑问,按照题目按照字节截取字符串,取一个字节的时候应为空,这里是我,所以这个程序还有待 改进。
下面这篇文字也有利于大家理解:
Sunshine 的 String.getBytes()和new String()
在Java中,String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示,如
byte[] b_gbk = "中".getBytes("GBK");
byte[] b_utf8 = "中".getBytes("UTF-8");
byte[] b_iso88591 = "中".getBytes("ISO8859-1");
byte[] b_unicode = "中".getBytes("unicode");
将分别返回“中”这个汉字在GBK、UTF-8和ISO8859-1编码下的byte数组表示,此时b_gbk的长度为2,b_utf8的长度为3,b_iso88591的长度为1,b_unicode 的长度为4(系统的的unicode采用的是big-endian就是前面是两个字节来表示这个的,unicode采用的都是两个字节编码,所以后面是4个字节 )。
而与getBytes相对的,可以通过new String(byte[], decode)的方式来还原这个“中”字时,这个new String(byte[], decode)实际是使用decode指定的编码来将byte[]解析成字符串。
String s_gbk = new String(b_gbk,"GBK");
String s_utf8 = new String(b_utf8,"UTF-8");
String s_iso88591 = new String(b_iso88591,"ISO8859-1");
通过打印s_gbk、s_utf8和s_iso88591,会发现,s_gbk和s_utf8都是“中”,而只有s_iso88591是一个不认识的字符,为什么使用ISO8859-1编码再组合之后,无法还原“中”字呢,其实原因很简单,因为ISO8859-1编码的编码表中,根本就没有包含汉字字符,当然也就无法通过"中".getBytes("ISO8859-1");来得到正确的“中”字在ISO8859-1中的编码值了,所以再通过new String()来还原就无从谈起了。
因此,通过String.getBytes(String decode)方法来得到byte[]时,一定要确定decode的编码表中确实存在String表示的码值,这样得到的byte[]数组才能正确被还原。
有时候,为了让中文字符适应某些特殊要求(如http header头要求其内容必须为iso8859-1编码),可能会通过将中文字符按照字节方式来编码的情况,如
String s_iso88591 = new String("中".getBytes("UTF-8"),"ISO8859-1"),这样得到的s_iso8859-1字符串实际是三个在ISO8859-1中的字符,在将这些字符传递到目的地后,目的地程序再通过相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正确的中文汉字“中”。这样就既保证了遵守协议规定、也支持中文。
编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输入"我ABC汉DEF",6,应该输出为"我ABC"而不是"我ABC+汉的半个"。
目前很多流行的语言,如C#、Java内部采用的都是 Unicode 16(UCS2)编码,在这种编码中所有的字符都是两个字符,因此,如果要截取的字符串是中、英文、数字混合的,就会产生问题,如下面的字符串:
String s = "a加b等于c,如果a等1、b等于2,那么c等3";
上面的字符串既有汉字,又有英文字符和数字。如果要截取前6个字节的字符,应该是”a加b等",但如果用substring方法截取前6个字符就成了"a 加b等于c"。产生这个问题的原因是将substring方法将双字节的汉字当成一个字节的字符(UCS2字符)处理了。
英文字母和中文汉字在不同的编码格式下,所占用的字节数也是不同的,我们可以通过下面的例子来看看在一些常见的编码格式下,一个英文字母和一个中文汉字分别占用多少字节。
Java代码
import java.io.UnsupportedEncodingException;
public class EncodeTest {
/**
* 打印字符串在指定编码下的字节数和编码名称到控制台
*
* @param s
* 字符串
* @param encodingName
* 编码格式
*/
public static void printByteLength(String s, String encodingName) {
System.out.print("字节数:");
try {
System.out.print(s.getBytes(encodingName).length);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println(";编码:" + encodingName);
}
public static void main(String[] args) {
String en = "A";
String ch = "人";
// 计算一个英文字母在各种编码下的字节数
System.out.println("英文字母:" + en);
EncodeTest.printByteLength(en, "GB2312");
EncodeTest.printByteLength(en, "GBK");
EncodeTest.printByteLength(en, "GB18030");
EncodeTest.printByteLength(en, "ISO-8859-1");
EncodeTest.printByteLength(en, "UTF-8");
EncodeTest.printByteLength(en, "UTF-16");
EncodeTest.printByteLength(en, "UTF-16BE");
EncodeTest.printByteLength(en, "UTF-16LE");
System.out.println();
// 计算一个中文汉字在各种编码下的字节数
System.out.println("中文汉字:" + ch);
EncodeTest.printByteLength(ch, "GB2312");
EncodeTest.printByteLength(ch, "GBK");
EncodeTest.printByteLength(ch, "GB18030");
EncodeTest.printByteLength(ch, "ISO-8859-1");
EncodeTest.printByteLength(ch, "UTF-8");
EncodeTest.printByteLength(ch, "UTF-16");
EncodeTest.printByteLength(ch, "UTF-16BE");
EncodeTest.printByteLength(ch, "UTF-16LE");
}
}
运行结果如下:
英文字母:A
字节数:1;编码:GB2312
字节数:1;编码:GBK
字节数:1;编码:GB18030
字节数:1;编码:ISO-8859-1
字节数:1;编码:UTF-8
字节数:4;编码:UTF-16
字节数:2;编码:UTF-16BE
字节数:2;编码:UTF-16LE
中文汉字:人
字节数:2;编码:GB2312
字节数:2;编码:GBK
字节数:2;编码:GB18030
字节数:1;编码:ISO-8859-1
字节数:3;编码:UTF-8
字节数:4;编码:UTF-16
字节数:2;编码:UTF-16BE
字节数:2;编码:UTF-16LE
UTF-16BE和UTF-16LE是UNICODE编码家族的两个成员。UNICODE标准定义了UTF-8、UTF-16、UTF-32三种编码格式,共有UTF-8、UTF-16、UTF-16BE、UTF-16LE、UTF-32、UTF-32BE、UTF-32LE七种编码方案。JAVA所采用的编码方案是UTF-16BE。从上例的运行结果中我们可以看出,GB2312、GBK、GB18030三种编码格式都可以满足题目的要求。下面我们就以GBK编码为例来进行解答。
我们不能直接使用String类的substring(int beginIndex, int endIndex)方法,因为它是按字符截取的。'我'和'Z'都被作为一个字符来看待,length都是1。实际上我们只要能区分开中文汉字和英文字母,这个问题就迎刃而解了,而它们的区别就是,中文汉字是两个字节,英文字母是一个字节。
以下是转载自http://www.javaeye.com/topic/216577的代码
import java.io.UnsupportedEncodingException; public class CutString { /** * 判断是否是一个中文汉字 * * @param c * 字符 * @return true表示是中文汉字,false表示是英文字母 * @throws UnsupportedEncodingException * 使用了JAVA不支持的编码格式 */ public static boolean isChineseChar(char c) throws UnsupportedEncodingException { // 如果字节数大于1,是汉字 // 以这种方式区别英文字母和中文汉字并不是十分严谨,但在这个题目中,这样判断已经足够了 return String.valueOf(c).getBytes("GBK").length > 1; } /** * 按字节截取字符串 * * @param orignal * 原始字符串 * @param count * 截取位数 * @return 截取后的字符串 * @throws UnsupportedEncodingException * 使用了JAVA不支持的编码格式 */ public static String substring(String orignal, int count) throws UnsupportedEncodingException { // 原始字符不为null,也不是空字符串 if (orignal != null && !"".equals(orignal)) { // 将原始字符串转换为GBK编码格式 orignal = new String(orignal.getBytes(), "GBK"); // 要截取的字节数大于0,且小于原始字符串的字节数 if (count > 0 && count < orignal.getBytes("GBK").length) { StringBuffer buff = new StringBuffer(); char c; for (int i = 0; i < count; i++) { c = orignal.charAt(i); buff.append(c); if (CutString.isChineseChar(c)) { // 遇到中文汉字,截取字节总数减1 --count; } } return buff.toString(); } } return orignal; } public static void main(String[] args) { // 原始字符串 String s = "我ZWR爱JAVA"; System.out.println("原始字符串:" + s); try { System.out.println("截取前1位:" + CutString.substring(s, 1)); System.out.println("截取前2位:" + CutString.substring(s, 2)); System.out.println("截取前4位:" + CutString.substring(s, 4)); System.out.println("截取前6位:" + CutString.substring(s, 6)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } 运行结果: 原始字符串:我ZWR爱JAVA 截取前1位:我 截取前2位:我 截取前4位:我ZW 截取前6位:我ZWR爱 |
下面是我修改后的代码,在我的机器上能正确运行:
import java.io.UnsupportedEncodingException; public class CutString { /** * 判断是否是一个中文汉字 * * @param c * 字符 * @return true表示是中文汉字,false表示是英文字母 * @throws UnsupportedEncodingException * 使用了JAVA不支持的编码格式 */ public static boolean isChineseChar(char c) throws UnsupportedEncodingException { // 如果字节数大于1,是汉字 // 以这种方式区别英文字母和中文汉字并不是十分严谨,但在这个题目中,这样判断已经足够了 return String.valueOf(c).getBytes("GBK").length > 1; } /** * 按字节截取字符串 * * @param orignal * 原始字符串 * @param count * 截取位数 * @return 截取后的字符串 * @throws UnsupportedEncodingException * 使用了JAVA不支持的编码格式 */ public static String substring(String orignal, int count) throws UnsupportedEncodingException { // 原始字符不为null,也不是空字符串 if (orignal != null && !"".equals(orignal)) { // 将原始字符串转换为GBK编码格式 //orignal = new String(orignal.getBytes(), "GBK");// // System.out.println(orignal); // System.out.println(new String(orignal.getBytes("GBK"),"UTF-8")); // 要截取的字节数大于0,且小于原始字符串的字节数 if (count > 0 && count < orignal.getBytes("GBK").length) { StringBuffer buff = new StringBuffer(); char c; for (int i = 0; i < count; i++) { c = orignal.charAt(i); buff.append(c); if (CutString.isChineseChar(c)) { // 遇到中文汉字,截取字节总数减1 --count; } } // System.out.println(new String(buff.toString().getBytes("GBK"),"UTF-8")); return new String(buff.toString().getBytes(),"UTF-8"); } } return orignal; } public static void main(String[] args) { // 原始字符串 String s = "我ZWR爱JAVA"; System.out.println("原始字符串:" + s); try { System.out.println("截取前1位:" + CutString.substring(s, 1)); System.out.println("截取前2位:" + CutString.substring(s, 2)); System.out.println("截取前4位:" + CutString.substring(s, 4)); System.out.println("截取前6位:" + CutString.substring(s, 6)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } |
下面这篇文字也有利于大家理解:
Sunshine 的 String.getBytes()和new String()
在Java中,String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示,如
byte[] b_gbk = "中".getBytes("GBK");
byte[] b_utf8 = "中".getBytes("UTF-8");
byte[] b_iso88591 = "中".getBytes("ISO8859-1");
byte[] b_unicode = "中".getBytes("unicode");
将分别返回“中”这个汉字在GBK、UTF-8和ISO8859-1编码下的byte数组表示,此时b_gbk的长度为2,b_utf8的长度为3,b_iso88591的长度为1,b_unicode 的长度为4(系统的的unicode采用的是big-endian就是前面是两个字节来表示这个的,unicode采用的都是两个字节编码,所以后面是4个字节 )。
而与getBytes相对的,可以通过new String(byte[], decode)的方式来还原这个“中”字时,这个new String(byte[], decode)实际是使用decode指定的编码来将byte[]解析成字符串。
String s_gbk = new String(b_gbk,"GBK");
String s_utf8 = new String(b_utf8,"UTF-8");
String s_iso88591 = new String(b_iso88591,"ISO8859-1");
通过打印s_gbk、s_utf8和s_iso88591,会发现,s_gbk和s_utf8都是“中”,而只有s_iso88591是一个不认识的字符,为什么使用ISO8859-1编码再组合之后,无法还原“中”字呢,其实原因很简单,因为ISO8859-1编码的编码表中,根本就没有包含汉字字符,当然也就无法通过"中".getBytes("ISO8859-1");来得到正确的“中”字在ISO8859-1中的编码值了,所以再通过new String()来还原就无从谈起了。
因此,通过String.getBytes(String decode)方法来得到byte[]时,一定要确定decode的编码表中确实存在String表示的码值,这样得到的byte[]数组才能正确被还原。
有时候,为了让中文字符适应某些特殊要求(如http header头要求其内容必须为iso8859-1编码),可能会通过将中文字符按照字节方式来编码的情况,如
String s_iso88591 = new String("中".getBytes("UTF-8"),"ISO8859-1"),这样得到的s_iso8859-1字符串实际是三个在ISO8859-1中的字符,在将这些字符传递到目的地后,目的地程序再通过相反的方式String s_utf8 = new String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正确的中文汉字“中”。这样就既保证了遵守协议规定、也支持中文。
相关文章推荐
- 面试题之java 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 要求不能出现截半的情况
- Java编写一个截取字符串的函数,输入一个为字符串和字节数,输出按字节截取的字符串。要求不能出现截半的情况
- java中按字节截取String字符串.
- 讲字符串按照字节截取 JAVA
- 按字节截取字符串-Java
- Java实现按字节长度截取字符串的方法
- Java按字节长度截取字符串
- java和Js 按字节来截取字符串长度(为了符合数据库varchar)
- 用Java实现按字节长度截取字符串的方法
- java根据字节 截取字符串,保证汉字不被截取半个
- Java按字节长度截取字符串
- "Unicode"编码,java实现一个按字节截取字符串的子串的方法
- java按字节截取中英文混合字符串
- java按字节截取字符串
- java按照字节截取字符串
- JAVA中按字节截取字符串
- java按字节长度截取字符串
- java中按字节数的长度来截取字符串,并以...来忽略
- Java按字节长度截取字符串
- 关于java按字节截取带有汉字的字符串的解法