byte[]与String互转不一致引起的bug

​​​公告:文章会第一时间发在微信公众号《魔笛手CTO》,欢迎关注

String s = "HelloWorld";
byte[] b = s.getBytes();
System.out.println(new String(b));

看到上面的代码,可能有同学会问了,这不就是JAVA的基本操作吗,你老张有什么好讲的?

既然这样,老张要考考大家,猜一下下面的代码执行之后会输出怎样的结果。

public class Hello {
    public static void main(String[] args){
        String src = "HelloWorld";
        String dest = encrypt(src);
        System.out.printf("原来字符转为字节的长度为:%d\n", src.getBytes().length);
        System.out.printf("加密字符转为字节的长度为:%d\n", dest.getBytes().length);
    }
    /*为了String进,String出,做了一次封装*/
    private static String encrypt(String s){
        byte[] b = encrypt(s.getBytes());
        return new String(b);
    }
    /*这里只是用位移演示加密,字节进,字节出*/
    private static byte[] encrypt(byte[] byteArr){
        for (int i=0; i<byteArr.length; i++){
            byteArr[i] <<= 1;
        }
        return byteArr;
    }
}

现在揭晓答案:

原来字符转为字节的长度为:10加密字符转为字节的长度为:26

不知道各位同学有没有猜对,说来惭愧,老张刚刚踩过这个坑。

 

事情的起因是这样的,由于Java没有内置的RC4加密,老张需要自己封装实现,实现的过程中用到了字节和字符的互相转换。因为RC4只是在针对的byte[]上进行位移操作,所以其输入输出的字节长度应该是相等的。但是老张为了使加密能够应用于字符串,用字符串对加密方法做了一层封装。问题就出在这一层封装上。

 

且听老张给你慢慢道来。

什么是字节?

位是计算机内部的最小数据储存单位,8位就组成了1字节,也就是java中的byte。

什么是字符?

字符是指字母、数字、特殊符号的集合,也就是java中的char和String。

字节怎么就变成了字符?

答案是字符编码。比较出名的字符集是ascii、gb2312、utf-8等。其中ascii和gb2312是定长字符编码,一个字符分别占用1字节、2字节,而utf-8是变长字符编码,最短为1字节,最常为4字节。

以ascii为例,字母A在计算机中的存储为0b0100 0001。

 

到这里可能有同学已经知道老张一开始天真在哪里了?

如果只是字符串转为字节流之后又接着转为字符串也没什么问题。老张天真就天真在对utf-8得到的字节流做了位移操作之后,还期望能够用utf-8重新解析字节流。

敲黑板:Java中String内置的public String(byte[] bytes)和public byte[] getBytes()对于不合法的utf-8字节流在解析时会增删字节。

 

希望各位同学引以为戒!

<think>我们正在讨论C#中字符串的使用和常见问题。字符串在C#中是一个非常重要的数据类型,因为它们在几乎所有应用程序中都被广泛使用。我将从以下几个方面进行介绍:1.字符串的基本特性和创建方式2.字符串的可变性及其影响3.常见的字符串操作4.字符串比较和相等性判断5.字符串格式化6.字符串性能(如StringBuilder的使用)7.常见问题及解决方法注意:在回答中,我会引用一些站内资料作为参考,并在末尾添加引用标识。首先,字符串在C#中是引用类型,但具有一些值类型的特性(比如可变性)。在C#中,字符串关键字是`string`(它是`System.String`的别名)。 ###1.字符串的基本特性和创建方式我们可以通过多种方式创建字符串: ```csharpstrings1= "Hello World"; //使用字符串字面量strings2= newstring('a',5);//创建由5个'a'组成的字符串,结果为"aaaaa" char[] chars= {'H', 'e','l', 'l','o' };string s3 =new string(chars); //从字符数组创建``` ###2.字符串的可变性字符串一旦创建,其内容就能更改。当我们对字符串进行修改(如连接、替换等)时,实际上是创建了一个新的字符串对象。例如:```csharpstring s="hello"; s+=" world"; //创建一个新的字符串对象,并将s指向这个新对象```这种特性使得频繁修改字符串(如在循环中拼接字符串)会导致性能问题,因为每次修改都会创建新的字符串对象。###3.常见的字符串操作C#提供了丰富的字符串操作方法,例如:-连接:`string.Concat`,`+`运算符-分割:`Split`方法-查找:`IndexOf`, `LastIndexOf`, `Contains`-替换:`Replace` -大小写转换:`ToUpper`, `ToLower` -修剪:`Trim`,`TrimStart`,`TrimEnd`-子串:`Substring`示例:```csharpstring str= "C# StringExample"; intindex= str.IndexOf("String"); //返回3string sub= str.Substring(3,6);//返回"String"string replaced= str.Replace("Example", "Demo");//返回"C# StringDemo"``` ###4.字符串比较和相等性判断在C#中,字符串比较通常有以下几种方式: -`==`运算符:比较字符串的内容(值),而是引用(因为字符串是引用类型,但`==`被重载了)。- `string.Equals`方法:可以指定是否区分大小写等。 -`string.Compare`和`string.CompareTo`:按字典顺序比较两个字符串,并返回一个整数表示它们的关系(小于0表示第一个字符串小于第二个,0表示相等,大于0表示大于)。注意:对于字符串比较,我们通常需要考虑文化区域(Culture)和大小写敏感性。例如:```csharpstring s1 ="hello"; strings2= "HELLO";bool b1 =s1==s2; //false,区分大小写bool b2 =string.Equals(s1, s2,StringComparison.OrdinalIgnoreCase);// true,区分大小写``` ###5.字符串格式化字符串格式化可以通过多种方式实现: -`string.Format`方法:使用占位符格式化字符串。-插值字符串:C#6.0引入,以`$`开头的字符串,可以直接在花括号内插入表达式。示例:```csharpstring name= "Alice";int age=25;//使用string.Formatstring s1 =string.Format("Myname is{0}, Iam {1}years old.", name, age); //使用插值字符串string s2 =$"Myname is{name}, Iam {age}years old."; ```###6.字符串性能由于字符串的可变性,频繁修改字符串(如拼接)会导致大量临时字符串对象的生成,从而增加垃圾回收(GC)的压力。在这种情况下,推荐使用`StringBuilder`类。 `StringBuilder`是一个可变的字符串类,它可以在同一个对象上进行修改,避免创建多个字符串对象。示例: ```csharpStringBuildersb =new StringBuilder(); for(inti =0;i <100; i++) {sb.Append(i.ToString());} stringresult =sb.ToString(); //最后再转换为字符串```###7.常见问题及解决方法-问题1:字符串连接性能问题。在循环中多次使用`+`连接字符串会导致性能下降。解决方法:使用`StringBuilder`。 -问题2:字符串比较时忽略大小写导致的问题。例如,在比较用户输入时,有时需要忽略大小写,但使用当可能引起性能或逻辑问题。建议使用`StringComparison`枚举来明确指定比较方式(如`StringComparison.OrdinalIgnoreCase`)。-问题3:字符串内存占用过大。由于字符串是可变的,如果程序中有大量字符串操作,可能会导致内存占用过高。解决方法:合理使用`StringBuilder`,及时释放再使用的字符串引用(设置为null),或者使用其他数据结构(如字符数组)来操作。 -问题4:字符串格式化错误。占位符数量参数数量匹配会导致运行时异常。使用插值字符串可以避免这个问题。引用说明:在讨论字符串操作时,我们参考了.NET框架中关于字符串的文档[^1]。在讨论字符串比较时,我们参考了关于字符串比较的最佳实践[^2]。在讨论字符串性能问题时,我们参考了关于垃圾回收和内存管理的资料[^5]。希望以上内容能帮助你理解C#中字符串的使用和常见问题。§§相关问题§§1.在C#中,如何正确地进行字符串比较以避免文化差异带来的问题?2.除了`StringBuilder`,还有哪些方法可以优化字符串操作的性能?3.C#中的字符串驻留(stringinterning)是什么?它如何影响程序性能?4.在C#中,字符串和字符数组之间如何高效转换?5.如何处理C#中的多行字符串?有哪些注意事项?</think>### C# 字符串使用指南常见问题 #### 一、字符串基础特性 1. **可变性 (Immutability)** C# 字符串是 `System.String` 类型,具有**可变性**。每次修改(拼接、替换等)都会创建新对象: ```csharp string s1 = "Hello"; s1 += " World"; // 创建新对象,原对象未被修改 ``` *影响:* 频繁修改会导致内存开销和性能问题[^1]。 2. **创建方式** - 字面量声明:`string s = "text";` - 字符数组转换:`new string(charArray)` - 格式化创建:`string.Format("{0}:{1}", key, value)` - 插值字符串(C# 6+):`$"Time: {DateTime.Now}"` #### 二、核心操作用法 1. **常用方法** ```csharp // 拼接 string fullName = string.Concat(firstName, " ", lastName); // 分割 string[] parts = "a,b,c".Split(','); // 结果: ["a", "b", "c"] // 查找替换 int index = text.IndexOf("search"); string clean = text.Replace("old", "new"); // 大小写转换 string upper = text.ToUpper(); // 注意文化敏感性 ``` 2. **比较判等** - **值比较**(推荐): ```csharp if (str1.Equals(str2, StringComparison.OrdinalIgnoreCase)) ``` - **引用比较**(慎用): ```csharp if (string.ReferenceEquals(str1, str2)) ``` *注意:* `==` 运算符被重载为值比较[^2]。 3. **字符串格式化** ```csharp // 数值格式化 $"{price:C2}" // 货币格式,如 $12.50 $"{weight:0.##} kg" // 保留两位小数 // 日期格式化 $"{DateTime.Now:yyyy-MM-dd}" ``` #### 三、性能优化技巧 1. **避免临时字符串** ```csharp // 错误做法(循环中创建大量临时对象) string result = ""; for (int i = 0; i < 1000; i++) result += i; // 每次迭代创建新对象 // 正确做法:使用 StringBuilder var sb = new StringBuilder(); for (int i = 0; i < 1000; i++) sb.Append(i); string output = sb.ToString(); // 仅一次内存分配 ``` 2. **预分配数组处理** 当需高频修改字符时: ```csharp char[] chars = text.ToCharArray(); chars[0] = 'X'; // 直接修改数组 string modified = new string(chars); ``` #### 四、常见问题解决方案 1. **内存泄漏陷阱** *场景:* 长期持有子字符串引用 ```csharp string large = GetLargeString(); // 10MB 字符串 string small = large.Substring(0, 10); // small 会阻止整个 10MB 内存被回收 ``` *解决方案:* ```csharp string small = new string(large.Substring(0, 10)); // 创建独立副本 ``` 2. **文化敏感性导致的 Bug** *案例:* 土耳其语环境下的 `ToUpper` ```csharp // "i".ToUpper() 在土耳其语中变成 "İ" 而非 "I" ``` *修复:* 指定文化无关比较 ```csharp text.ToUpperInvariant(); string.Equals(a, b, StringComparison.Ordinal); ``` 3. **空值处理** - 使用 `string.IsNullOrEmpty()` - 使用 `string.IsNullOrWhiteSpace()`(包含空格检查) - 空合并运算符:`string s = input ?? "default"` 4. **编码问题** *场景:* 网络传输或文件读写时乱码 ```csharp // 正确转换字节数组 byte[] data = Encoding.UTF8.GetBytes(text); string decoded = Encoding.UTF8.GetString(data); ``` #### 五、调试建议 使用内存分析工具(如 Visual Studio 诊断工具)检查: - 字符串重复分配 - 大对象堆(LOH)压力 - 驻留字符串内存占用[^5] > 关键原则:**优先考虑可变性带来的安全性,仅在性能热点处优化**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值