【C#进阶系列】14 字符、字符串和文本编码
本来写了蛮多的,结果因为重启了一下机器导致写的东西都没了。
然后再回想之前写了什么,反而更像是把知识提炼了一番。
关于字符
字符什么的只要记住.net里面都用的Unicode编码就好。字符和数字之间转换用强制转换是最简单且高效的,
字符串是引用类型,存在与堆上,然而同一般的对象用newobj这个IL指令创建不同,字符串由ldstr指令创建。(load string)
关于字符串
字符串是不可变的,所有的String的方法都是创建一个新的字符串。
用+号去拼接字符串,会在堆上创建多个string对象,而堆上的对象考虑到垃圾回收就会影响性能,所以建议用StringBuilder去拼接。
字符串比较
虽然String提供了一堆比较方法,并且《CLR via C#》这本书的作者也推荐用这些比较,因为==和!=这种比较方式调用者并没有显式指出用什么规则来比较,而如果显示地指出以什么规则来比较,代码容易阅读和维护。
var str1 = "字符串1";
var str2 = "字符串2";
bool result1= str1.Equals(str2, StringComparison.OrdinalIgnoreCase);//使用序号排列(就是说对本地语言文化不敏感),并忽略大小写来比较。
bool result2 = str1 == str2;//常用的比较
然而,于我而言,确实是==更加明了,这个就看个人了。这些用方法比较的时候确实在有些多语言文化的场景比较好用,然而对于一般场景,我个人认为==更好一点,起码我自己看起来更好阅读和理解。
以StringComparison.Ordinal规则比较的话,CLR会快速先比较字符数量,数量相同才继续比较单独字符。而如果执行语言文化敏感的话,即使数量不同也有可能相等,所以一开始就会比较单个字符,这样就很耗性能。
System.StringComparer类也能执行字符串比较,它适用于大量不同字符串反复执行同一种比较。
字符串留用
CLR可通过一个String对象共享多个完全一致的String内容,这样就减少了字符串数量,节省内存,这就是字符串留用。
在.NET 4.5中自然会在程序集加载时对代码中的字面量字符串进行字符串留用,然而之前的版本就需要手动了。
String.Intern方法就是字符串留用的方法,将字符串加入一个哈希表中,如果哈希表中有就不加入,没有就加入。
这样当然可以减少内存,因为以后只引用一个字符串对象。但是要明白留存字符串这个操作也是需要消耗性能的。所以具体情况具体分析,还是需要慎重使用字符串留存。
字符串池
对于所有的字面量字符串中,相同内容的字符串,实际上都是引用的字符串池中的一个字符串。这是在C#编译器编译的时候就已经弄好的。
高效率构造字符串——StringBuilder
StringBuilder从字面意义上就很好理解了,字符串拼接什么的就用它好了。可以认为里面就是一个字符数组。
然而要理解StringBuilder的以下概念
- 最大容量(MaxCapacity)
- 指定了字符串中的最大字符数。默认值是Int32.MaxValue(约20亿)。
- 一般不用理会,除非是要限制一个字符串的最大字符数。
- 容量(Capacity)
- 前面说到,可以将StringBuilder里面认为是一个字符数组,那么容量就指定了当前字符数组的长度。
- 为什么说是当前呢?因为如果字符串拼接后超过了这个容量值,那么容量就会自动*2,且用新容量来分配新数组,并将原始数组中的字符串复制到新数组中。随后原始数据被垃圾回收。
- 所以看到这里你就应该很明白一点,用StringBuilder最好在开始的时候自己预估一个容量,最起码不要让他频繁扩容,要不然真是坑,还不如用String。
- 字符数组
- 也就是StringBuilder里面由Char结构构成的数组。
- 它的长度用Length获得
一般用用Append和AppendFormat进行追加字符串,当然也有其它的操作,只要明白里面操作的是一个数组就好。
虽然本书还介绍了一些字符串格式化和解析字符串的方式,然而
字符串编码——字符和字节的相互转换
对于使用汉字的我们使用字符串的话用Unicode没什么影响,因为汉字就占两个字节,然而对于英文字符实际上仅仅用一个字节就够了,但是在Unicode中还是会占两个字节,其中一个字节用于表示这个英文字符,另一个字节干脆就是\0。
所以一些英文翻译啊什么的,或者一大段英文文章的传送,那么将这些Unicode字符串编码成压缩的字节数组传送起来更有效率。
通常也就是用System.IO.BinaryWriter或者System.IO.StreamWriter时,需要进行编码,相应的读取时也需要解码。
一般不指定一种编码方案,那么就默认为UTF-8。(可以简单理解为中文两个字节,英文一个字节)
还有一种常用编码方案是UTF-16,也就是中英文都是两个字节,也被称为Unicode编码。(对于汉字而言,其实用UTF-16编码,比UTF-8更快)
其它的编码方式就不说了,对于我们而言基本上都是坑。
当我们进行编码时尽量用Encoding.Unicode获取编码方案构造对象,而不是用System.Text.UnicodeEncoding这种。
因为前者如果之前有请求会直接返回上次请求的对象给你,不会为每个请求构造新的对象。
而后者每次都会在托管堆中创建新的对象,所以会对性能有所影响。然而在System.Text中的这些派生自Encoding的编码类有特殊的构造器可以在对无效序列解码时抛出异常,所以如果要保证安全性,防范无效输入那么用后面这种比较好。
获取了这些编码方案构造对象后就可以利用GetBytes和GetString来将字符串转换为字节数组和将字节数组转换为字符串。
字节流的编码
就是通过System.Net.Sockets.NetworkStream对象读取一个UTF-16编码字符串,因为这种字节流通常以数据块形式传输,而如果一次从流中读取5个字节,而不是2的倍数的字节数,那么就可能会造成数据损坏。
所以可以用Encoding.Unicode.GetDecoder()获取一个新的构造对象,这个对象含有GetChars和GetCharCount两个方法。调用GetChars时它会尽可能多的解码,如果解码数组的字节不足以完成一个字符时,那么剩余的字符会保存到这个Decoder内部,下次调用它时,此Decoder会利用之前剩余的字节,再加上传给它的字节数组来进行解码。从流中读取Decoder对象的作用很大。
相反的编码一样。
以下为简单的Decoder解码示例
string strTroy = "奇葩";
Byte[] bytesTroy = Encoding.Unicode.GetBytes(strTroy);//形成长度为4的字节数组
Byte[] b1 = { bytesTroy[], bytesTroy[], bytesTroy[] };//一个奇,半个葩
Byte[] b2 = { bytesTroy[] };//半个葩
//以上操作算是模拟了按数据块获取,接下来
var decoder = Encoding.Unicode.GetDecoder();
char[] result=new char[];//解码后的字符数组
var charindex = decoder.GetCharCount(b1, , b1.Length);//若解码b1能形成的字符个数
decoder.GetChars(b1,, b1.Length, result, , false);//第一个0为从b1第0个位置开始解码,第二个0是从result的第0个位置开始写入
decoder.GetChars(b2,, b2.Length, result, charindex, false);
Console.WriteLine(string.Join("",result));//奇葩
安全字符串
System.Security.SecureString类,就是一个更安全的字符串类。
构造这个类的对象后,会在内部分配一个非托管内存块,以避开垃圾回收器。
和String对象不同,SecureString对象在回收后加密字符串的内容将不再存在于内存中。
当然这样的字符串如果不是信用卡啊密码什么的就不需要,毕竟会有性能影响。
【C#进阶系列】14 字符、字符串和文本编码的更多相关文章
- [Clr via C#读书笔记]Cp14字符字符串和文本处理
Cp14字符字符串和文本处理 字符 System.Char结构,2个字节的Unicode,提供了大量的静态方法:可以直接强制转换成数值: 字符串 使用最频繁的类型:不可变:引用类型,在堆上分配,但是使 ...
- <NET CLR via c# 第4版>笔记 第14章 字符,字符串和文本处理
14.1 字符 三种数值类型与 Char 实例的相互转换: static void Main() { Char c; Int32 n; //方法一: 通过C#转型(强制类型转换)实现数字与字符的相互转 ...
- clr from c# 字符 ,字符串 和 文本处理
1,字符----------在.net中,字符总是16位的Unicode代码值.每个字符都是一个System.Char结构(值类型)的一个实列. using System; public class ...
- .Net进阶系列(14)-异步多线程(async和await)(被替换)
1. 方法名前只有async,但是方法中Task实例前没有await关键字,该方法和普通方法没有什么区别,但是会报一个警告. #region 情况一 /// <summary> /// ...
- C#进阶系列 ---- 《CLR via C#》
[C#进阶系列]30 学习总结 [C#进阶系列]29 混合线程同步构造 [C#进阶系列]28 基元线程同步构造 [C#进阶系列]27 I/O限制的异步操作 [C#进阶系列]26 计算限制的异步操作 ...
- 《Python CookBook2》 第一章 文本 - 过滤字符串中不属于指定集合的字符 && 检查一个字符串是文本还是二进制
过滤字符串中不属于指定集合的字符 任务: 给定一个需要保留的字符串的集合,构建一个过滤函数,并可将其应用于任何字符串s,函数返回一个s的拷贝,该拷贝只包含指定字符集合中的元素. 解决方案: impor ...
- 重温CLR(十) 字符、字符串和文本处理
本章将介绍.net中处理字符和字符串的机制 字符 在.NET Framewole中,字符总是表示成16位Unicode代码值,这简化了国际化应用程序的开发. 每个字符都表示成System.Char结构 ...
- 13、如何拆分含有多种分隔符的字符串 14、如何判断字符串a是否以字符串b开头或结尾 15、如何调整字符串中文本的格式 16、如何将多个小字符串拼接成一个大的字符串
13.如何拆分含有多种分隔符的字符串 import re s = "23:41:2314\1234#sdf\23;" print(re.split(r'[#:\;]+',s)) ...
- C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)
前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...
随机推荐
- VS2015的一些资料
http://blog.csdn.net/hk_5788/article/details/48466295 主要看一下js支持方面的,另外今天复习了promise,刚入职的时候看得有些问题,今晚抽时间 ...
- List.Foreach与C#的foreach的区别
几年前参加面试时就被提问过,现在面试别人时也经常提到这个问题. 今天小试了一下.得出如下几点: 1. 首先,mscorlib里System.Collections.Generic. List<T ...
- Docker容器入门
为什么要看docker 从去年起就或多或少的接受了docker的熏陶,主要还是Infoq在去年有很多关于docker的实践视频讲座,记得有一篇是<Docker在雪球的技术实践>,当时听的也 ...
- SqlServer 错误1053:服务并未及时响应启动或控制请求
sqlserver 的登录用户修改成域账户后,启动不了 解决方法: 计算器管理选择管理员组 将域账户加入到管理员组即可
- rabbitmq消息队列——"发布订阅"
三."发布订阅" 上一节的练习中我们创建了一个工作队列.队列中的每条消息都会被发送至一个工作进程.这节,我们将做些完全不同的事情--我们将发送单个消息发送至多个消费者.这种模式就是 ...
- 对于System.Net.Http的学习(二)——使用 HttpClient 进行连接
对于System.Net.Http的学习(一)——System.Net.Http 简介 使用 HttpClient 进行连接 使用 System.Net.Http 命名空间中的 HttpClient ...
- AMD 和 CMD 的区别有哪些?
看到玉伯在介绍seajs和requirejs时,说“RequireJS 遵循的是 AMD(异步模块定义)规范,SeaJS 遵循的是 CMD (通用模块定义)规范”. 能否详细(举例)说明下这个2个规范 ...
- Struts2第一个例子Hello World!
1.首先用eclipse新建一个动态web项目Struts2Demo1: (把Default output floder由build\classes改成WebContent\WEB-INF\class ...
- Angularjs 跳转页面并传递参数的方法总结
http://www.zhihu.com/question/33565135 http://www.codeproject.com/Articles/1073780/ASP-NET-MVC-CRUD- ...
- Redis应用场景-转载
1. MySql+Memcached架构的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的 ...