前言

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

二进制转换问题

假设现在我们有一个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实现蓝桥杯方格计数

    标题:方格计数 如图p1.png所示,在二维平面上有无数个1x1的小方格. 我们以某个小方格的一个顶点为圆心画一个半径为 50000 的圆. 你能计算出这个圆里有多少个完整的小方格吗? 注意:需要提交 ...

  2. Java实现微生物增殖

    微生物增殖 假设有两种微生物 X 和 Y X出生后每隔3分钟分裂一次(数目加倍),Y出生后每隔2分钟分裂一次(数目加倍). 一个新出生的X,半分钟之后吃掉1个Y,并且,从此开始,每隔1分钟吃1个Y. ...

  3. java实现第六届蓝桥杯移动距离

    移动距离 题目描述 X星球居民小区的楼房全是一样的,并且按矩阵样式排列.其楼房的编号为1,2,3- 当排满一行时,从下一行相邻的楼往反方向排号. 比如:当小区排号宽度为6时,开始情形如下: 1 2 3 ...

  4. IOS App如何调用python后端服务

    本篇文章旨在通过一个小的Demo形式来了解ios app是如何调用python后端服务的,以便我们在今后的工作中可以清晰的明白ios app与后端服务之间是如何实现交互的,今天的示例是拿登录功能做一个 ...

  5. 【JVM故事】了解JVM的结构,好在面试时吹牛

    class文件格式 参考上一篇文章<[JVM故事]一个Java字节码文件的诞生记>,后续还会专门讲解class文件的内部结构. 数据类型 jvm包括两种数据类型,基本类型和引用类型. 基本 ...

  6. 如何在Vim中的查找替换

    “%s/最/第二/g” 用vim的人都知道,这是把所有“最”替换成“第二”的意思.其实vim的查找替换功能非常强大,用的好可以极大提升效率. vim的查找替换命令如下所示:    :{作用范围}s/{ ...

  7. Windows安装多个python解释器

    Windows安装多个python解释器 ​ 在windows10系统下安装两个不同版本的的python解释器,在通常情况下编译执行文件都是没问题的,但是加载或下载包的时候pip的使用就会出现问题,无 ...

  8. 解决Zabbix 5.0不能选择中文和中文乱码问题

    Zabbix web界面不能选择中文,提示: You are not able to choose some of the languages, because locales for them ar ...

  9. TensorFlow从0到1之TensorFlow损失函数(12)

    正如前面所讨论的,在回归中定义了损失函数或目标函数,其目的是找到使损失最小化的系数.本节将介绍如何在 TensorFlow 中定义损失函数,并根据问题选择合适的损失函数. 声明一个损失函数需要将系数定 ...

  10. 消息队列——Kafka基本使用及原理分析

    文章目录 一.什么是Kafka 二.Kafka的基本使用 1. 单机环境搭建及命令行的基本使用 2. 集群搭建 3. Java API的基本使用 三.Kafka原理浅析 1. topic和partit ...