zoukankan      html  css  js  c++  java
  • 十进制和26进制转换算法

    最近在封装Excel组件,需要提供两个接口,分别根据单元索引和单元名称访问单元格。例如,GetCell(1, 2)和GetCell(“A2”),这两种方法返回的结果是相同的。这里遇到一个问题,如何在单元索引([1,2])和单元名称(A2)之间相互转换?
    由于在单元索引和单元名称中,行号是相同的,所以我们只需要转换列号就可以了。本来以为是个很简单的问题,结果调试了好长时间才搞定。于是写了这篇文章,总结一下。

    【问题描述】

    在Excel中,列的名称是这样一个递增序列:A、B、C、…、Z、AA、AB、AC、…、AZ、BA、BB、BC、…、BZ、CA、…、ZZ、AAA、AAB…。
    我们需要将上述列名序列和以下自然数序列相互转换:1、2、3、…。

    【问题分析】

    经过分析,我们不难发现,这是一个26进制和十进制相互转换的问题。序列A-Z依次对应序列1-26。进制转换的基本办法就是“取余法”,换算规则如下:
    ABZ = 1*26² + 2 * 26¹ + 26*26°= 676 + 52 + 26 = 754
    于是,我们就知道该如何设计一个十进制转换为26进制的算法了。

    【算法描述】
    Step1.[取余] 用指定自然数n除以26,得到一个余数m。如果m = 0,置m←26。
    Step2.[转换为字符] 将m映射为字符c,映射规则是{1-26}->{A-Z}。然后将c拼接到26进制值s的左边,也就是置s←c + s。
    Step3.[去余降幂] 置n←(n–m)/26。如果n > 0,则回到Step1继续执行,否则进入Step4。
    Step4.[结束] 返回s。

    按照上述思想,26进制转换为十进制的过程正好是相反的,而且实现起来也更为简单,在此不述。

    C#源代码如下:

    /// <summary>
    /// 将指定的自然数转换为26进制表示。映射关系:[1-26] ->[A-Z]。
    /// </summary>
    /// <param name="n">自然数(如果无效,则返回空字符串)。</param>
    /// <returns>26进制表示。</returns>
    public static string ToNumberSystem26(int n){
        string s = string.Empty;
        while (n > 0){
            int m = n % 26;
            if (m == 0) m = 26;
            s = (char)(m + 64) + s;
            n = (n - m) / 26;
        }
        return s;
    } 
    
    /// <summary>
    /// 将指定的26进制表示转换为自然数。映射关系:[A-Z] ->[1-26]。
    /// </summary>
    /// <param name="s">26进制表示(如果无效,则返回0)。</param>
    /// <returns>自然数。</returns>
    public static int FromNumberSystem26(string s){
        if (string.IsNullOrEmpty(s)) return 0; 
        int n = 0;
        for (int i = s.Length - 1, j = 1; i >= 0; i--, j *= 26){
            char c = Char.ToUpper(s[i]);
            if (c < 'A' || c > 'Z') return 0;
            n += ((int)c - 64) * j;
        }
        return n;
    }
    
    static void Main(string[] args){
        int[] numbers = { 1, 10, 26, 27, 256, 702, 703 };
        foreach (int n in numbers){
            string s = ToNumberSystem26(n);
            Console.WriteLine(n + "\t" + s + "\t" + FromNumberSystem26(s));
        }
        Console.ReadLine();
    }

    运行结果如下图所示:


    【总结】
    一般情况下,各种进制都是以0为起点递增的,例如,八进制(0-7),16进制(0-F)。在本文中,我们设计的26进制是以A为起点的,而不是0,这是主要的不同点。

  • 相关阅读:
    LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别
    Linq扩展方法之All 、Any
    Linq扩展方法之Aggregate 对序列应用累加器函数
    C#开发的进化史
    C#选择文件、选择文件夹、打开文件(或者文件夹)
    XPath操作XML文档
    文档对象模型操作xml文档
    XML简介
    php获取某经纬度附近地点位置
    Yii中实例化类的四种方式
  • 原文地址:https://www.cnblogs.com/lavezhang/p/2499000.html
Copyright © 2011-2022 走看看