前言

最近遇到很有意思转换二进制的问题,有部分童鞋俨然已了解,可能也有一部分童鞋没碰到过也就不知情,这里我们来深入学习下转换二进制所带来的问题。

二进制转换问题

假设现在我们有一个int类型的数据,它的范围区间暂且定在0-15之间,我们需要将其转换为二进制,然后获取二进制中的每一位,若不足4位则0填充。看似很简单是不是,直接通过C#内置APi即可达到此需求,如下:

 var binary = Convert.ToString(, ).PadLeft(, '').ToArray();

上述将数字7转换为包含二进制位的字符串数组形式,7转换二进制然后不足4位以0填充即(0111),我们如下获取二进制位字符串数组为索引的位,结果应该打印出0,对吗?

var zerobit = binary[];
Console.WriteLine(zerobit);

好像一点毛病也没有,这是在控制台中进行打印,若是将该数据导出到Excel中,你会发现结果将可能是48或49而不是0或1(你可以一试)这是因为如下:

我们通过调试可知实际上在字符0上还携带有48,这个48实际上是字符0的ASCII码,字符1的ASCII码是49,通过如下代码即可证明:

foreach (var b in System.Text.Encoding.UTF8.GetBytes(binary))
{
Console.WriteLine(b.ToString());
}

我们对将对应字符数组索引数据进行如下ToString转换即可避免导出数据时可能出现的问题

var zerobit = binary[];
Console.WriteLine(zerobit.ToString());

转换字符数组问题

当我们转换为字符数组时,有两种方式,既可采用上述ToArray方法,也可以通过ToCharArray方法来实现,如下,那么哪种方法会更好呢?

var binary = Convert.ToString(, ).PadLeft(, '').ToArray();

var binary1 = Convert.ToString(, ).PadLeft(, '').ToCharArray();

此时比较此二者方法的性能好坏,只能去看对应源码实现,首先我们来看看ToCharArray方法,如下:

public unsafe char[] ToCharArray()
{
if (Length == )
{
return Array.Empty<char>();
}
char[] array = new char[Length];
fixed (char* smem = &_firstChar)
{
fixed (char* dmem = &array[])
{
wstrcpy(dmem, smem, Length);
}
}
return array;
}
internal unsafe static void wstrcpy(char* dmem, char* smem, int charCount)
{
Buffer.Memmove((byte*)dmem, (byte*)smem, (uint)(charCount * ));
}

上述对于ToCharArray代码量还是不多,我们来看看ToArray方法实现,如下:

public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
return new Buffer<TSource>(source).ToArray();
}

上述只是写了一个扩展方法,我们继续往下看Buffer类的具体实现,如下:

internal Buffer(IEnumerable<TElement> source)
{
TElement[] array = null;
int num = ;
ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null)
{
num = collection.Count;
if (num > )
{
array = new TElement[num];
collection.CopyTo(array, );
}
}
else
{
foreach (TElement item in source)
{
if (array == null)
{
array = new TElement[];
}
else if (array.Length == num)
{
TElement[] array2 = new TElement[checked(num * )];
Array.Copy(array, , array2, , num);
array = array2;
}
array[num] = item;
num++;
}
}
items = array;
count = num;
}

从代码量上看就觉得ToArray方法实现稍微复杂一点,所以我们选择使用ToCharArray会更好,我要是如此草草结束此文,一定会喷。原归正传,我们一步步来分析,如上做了一点优化,首先会判断参数是否属于集合接口,若是则直接通过复制转换为数组形式,但是我们知道字符串肯定没有实现ICollection<T>接口,所以走另外一个条件分支,但是有的童鞋可能就有疑问了,此时为何可以遍历呢?那是因为针对字符实现了IEnumerable<char>接口,所以可以进行遍历,如下:

public sealed class CharEnumerator : IEnumerator, ICloneable, IEnumerator<char>, IDisposable
{......}

接下来则是初始化容量为4的数组,为何这里为4呢?这里我认为应该谈不上优化,与其说是实现者的一种拍脑袋的想法,我倾向于理解为是一种权衡或考量,既然转到此分支说明一定是转换为二进制位的数组,比如上述进行填充后长度刚好为4。再接下来无用我再多讲,就是遍历所有字符数组,将每一个字符串添加到数组中去,直到数组长度和变量值(num)相等最终进行一次性复制,最终将数组赋值给数组元素以及将变量num赋值给数组元素的数量(count)。

好了,讲解了这么多,那么问题来了,到底谁的性能会更好呢?ToCharArray方法实现底层采用指针操作转化为字符数组,而利用ToArray方法由于string没有实现ICollection<T>接口,也就是说根本不清楚字符串中字符数组的长度,所以只能采取低效遍历的方式去进行转换,我们可认为通过中间缓冲区的方式(即上述通过实例化数组作为桥梁最终进行复制)实现。由此得出,在将字符串转换为字符数组时,一定要用ToCharArray方法而不是ToArray,ToCharArray性能优于ToArray方法,我不禁在想,针对字符转换为数组只提供ToCharArray方法不就好了么,为何还要提供ToArray方法,让人容易产生误会,它的场景难道还有其他吗?

总结

本文详细讲解了在转换二进制数据所引发的一点个人思考,在将字符串转换为字符数组时,通过方法名称意思可能直接就用ToCharArray方法,但是又偏偏提供了字符串的ToArray方法,其本质是针对字符数组的扩展方法,如果对源码不了解的话,根本就不清楚到底应该用哪一个,从性能角度讲,ToCharArray方法优于ToArray方法,至于最终用哪一个,你说了算。

C#由转换二进制所引起的思考,了解下?的更多相关文章

  1. Formiko总结整数十进制转换二进制原理

    引子: 为什么十进制转二进制的“辗转相除记录余数倒序输出”的算法是正确的?这个问题陪伴了Formiko半年. 实践: 实践一:把十进制数100转换成二进制数的图   上图和和下图唯一的区别在最后一位上 ...

  2. Python 进制转换 二进制 八进制 十进制 十六进制

    Python 进制转换 二进制 八进制 十进制 十六进制 作者:方倍工作室 地址:http://www.cnblogs.com/txw1958/p/python3-scale.html 全局定义一定不 ...

  3. Java 进制转换(二进制(负),八进制,十进制,十六进制),位运算、逻辑运算(2)

    负数的二进制表现形式:其实就是该数的绝对值取反+1. 进制转换(二进制,八进制,十进制,十六进制),原理解析 十六进制的表现形式: (2)(与.异或.左移.右移.三元运算符)

  4. python的进制转换二进制,八进制,十六进制及其原理

    #!usr/bin/env python# coding:utf-8def binary(): '''二进制的方法与算法'''    Number = 10    Number1 = 20    Nu ...

  5. IOS 图片转换二进制 二进制转换为图片

    //类方法 图片 转换为二进制 +(NSData *)Image_TransForm_Data:(UIImage *)image { NSData *imageData = UIImageJPEGRe ...

  6. C# 进制转换(二进制、十六进制、十进制互转)

    原文地址:https://www.cnblogs.com/icebutterfly/p/8884023.html C# 进制转换(二进制.十六进制.十进制互转)由于二进制数在C#中无法直接表示,所以所 ...

  7. 一次线上Redis类转换异常排查引发的思考

    之前同事反馈说线上遇到Redis反序列化异常问题,异常如下: XxxClass1 cannot be cast to XxxClass2 已知信息如下: 该异常不是必现的,偶尔才会出现: 出现该异常后 ...

  8. C# 进制转换(二进制、十六进制、十进制互转) 转载 https://www.cnblogs.com/icebutterfly/p/8884023.html

    C# 进制转换(二进制.十六进制.十进制互转)由于二进制数在C#中无法直接表示,所以所有二进制数都用一个字符串来表示例如: 二进制: 1010 表示为 字符串:"1010" int ...

  9. xxd命令转换二进制十六进制文件

    Linux下的xxd命令,可以把文件在二进制和十六进制之间互相转换. 1.准备需要转换的二进制文件 这个二进制文件可以是任意格式的, 示例中我们创建一个txt格式的二进制文件, vi demo.txt ...

随机推荐

  1. Java实现 LeetCode 290 单词规律

    290. 单词规律 给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律. 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非 ...

  2. Java实现 LeetCode 96 不同的二叉搜索树

    96. 不同的二叉搜索树 给定一个整数 n,求以 1 - n 为节点组成的二叉搜索树有多少种? 示例: 输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树: 1 3 ...

  3. java实现 洛谷 P1540 机器

    import java.util.LinkedList; import java.util.Scanner; public class Main { private static Scanner ci ...

  4. 更多的bash shell命令

    1.探查进程:ps 2.实时监控进程:top 3.结束进程:kill.killall 4.查看挂载媒体:mount 5.移除设备:nmount 6.查看已挂载设备的使用情况:df 7.显示特定磁盘的使 ...

  5. django——bbs

    今日内容概要 bbs是一个前后端不分离的全栈项目,前端和后端都需要我们自己一步步的完成 表创建及同步 注册功能 forms组件 用户头像前端实时展示 ajax 登陆功能 自己实现图片验证码 ajax ...

  6. 如何在Centos7安装rabbitmq的PHP扩展

    1.先安装rabbitmq-c, wget https://github.com/alanxz/rabbitmq-c/releases/download/v0.8.0/rabbitmq-c-0.8.0 ...

  7. win10 设置开机启动VMware虚拟机并打开指定镜像

    1.设置win10开机启动应用 把应用程序的‘快捷方式’放到“系统启动文件夹”里即可. 2.开机启动VMware虚拟机并打开指定镜像 a.右键VMware Workstation快捷方式,看属性 b. ...

  8. Activiti6 学习日志(一):整合 SpringBoot2.1.3

    本章节记录整合过程和部分问题,目前整合并不完美后续会继续更新... 文档链接: 5.2.1 activiti用户手册 activiti用户手册 activiti6 API 技术栈: springboo ...

  9. Linux系统管理——Linux安装

    实验软件包下载地址 VirtualBox下载地址 VirtualBox:下载地址 CentOS7镜像下载地址 CentOS7:下载地址 远程登录管理工具下载地址 MobaXterm:下载地址 Virt ...

  10. 使用iText生成pdf文件

    前言 折腾了一早上的iText,下面主要介绍一下如何使用iText通过java代码生成pdf文档,以及如何输出包含中文的pdf文档. 首先,要说明的是,我用的是iText-7(java),下载链接是: ...