简化excel单元列表以创建Range

时间:2012-08-29 06:54:22

标签: c# algorithm excel vsto

我想从动态生成的单元格引用列表中创建一个Excel.Range对象。

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing);

由于strCellRange可能变得非常大,因此它会提供com异常。 因此,我希望简化它以使用union进行范围表示法。

e.g。

string strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27";

string strCellRange = "F2:L2,F7:L7,F12:L12,F17:L17,F22:L22,F27:L27";
  1. 是否有任何Excel方法可以创建具有大量单元格引用的Range对象?
  2. 是否有一种已知算法可以实现上述简化(矩阵算法)?

5 个答案:

答案 0 :(得分:3)

加扬,

VBA中,你可以强制你的直接字符串到

的范围
Sub Test()
Dim rng1 As Range
Dim strCellRange As String
strCellRange = "F2,G2,H2,I2,J2,K2,L2,F7,G7,H7,I7,J7,K7,L7,F12,G12,H12,I12,J12,K12,L12,F17,G17,H17,I17,J17,K17,L17,F22,G22,H22,I22,J22,K22,L22,F27,G27,H27,I27,J27,K27,L27"
Set rng1 = Range(strCellRange)
Set rng1 = Union(rng1, rng1)
Debug.Print rng1.Address
End Sub

答案 1 :(得分:2)

这可能是一个起点(它不适用于Z以外的列,并且不能识别矩形):

    private string CompactRangeStringByRows(string strCellRange)
    {
        SortedDictionary<int, SortedList<char, char>> rows = new SortedDictionary<int, SortedList<char, char>>();
        foreach (string aCell in strCellRange.Split(new Char[] { ',' }))
        {
            char col = aCell[0];
            int row = int.Parse(aCell.Substring(1, aCell.Length - 1));
            SortedList<char, char> cols;
            if (!rows.TryGetValue(row, out cols))
            {
                cols = new SortedList<char, char>();
                rows[row] = cols;
            }
            cols.Add(col, col);
        }
        StringBuilder sb = new StringBuilder();
        bool first = true;
        foreach (KeyValuePair<int, SortedList<char, char>> rowCols in rows)
        {
            char minCol = '0';
            char maxCol = '0';
            foreach (char col in rowCols.Value.Keys)
            {
                if (minCol == '0')
                {
                    minCol = col;
                    maxCol = col;
                }
                else
                {
                    if (col == maxCol + 1)
                        maxCol = col;
                    else
                    {
                        AddRangeString(sb, first, rowCols.Key, minCol, maxCol);
                        minCol = col;
                        maxCol = col;
                        first = false;
                    }
                }
            }
            AddRangeString(sb, first, rowCols.Key, minCol, maxCol);
            first = false;
        }
        return sb.ToString();
    }

    private void AddRangeString(StringBuilder sb, bool first, int row, char minCol, char maxCol)
    {
        if (!first)
            sb.Append(',');
        sb.Append(minCol);
        sb.Append(row);
        if (maxCol != minCol)
        {
            sb.Append(':');
            sb.Append(maxCol);
            sb.Append(row);
        }
    }

答案 2 :(得分:1)

<强> VBA

Function Unionize(src As Range) As Range
Dim cell As Range
Dim unionizedRange As Range

For Each cell In src
    If unionizedRange Is Nothing Then
        Set unionizedRange = cell
    Else
        Set unionizedRange = Union(unionizedRange, cell)
    End If
Next

Set Unionize = unionizedRange
End Function

c#(粗剪,没有通过编译器运行语法)

Excel.Range Unionize(Excel.Range src)
{
    Excel.Range unionizedRange;

    foreach (Excel.Range cell in src)
    {
        if (unionizedRange == null)
        {
            unionizedRange = cell;
        }
        Else
        {
            unionizedRange = Application.Union(unionizedRange, cell);
        }
    }
    return unionizedRange;
}

编辑:基于@ brettdj的解决方案

Excel.Range outputRange = sheet.get_Range(strCellRange, Type.Missing);
strCellRange = Application.Union(outputRange, outputRange).Address(false, false);

答案 3 :(得分:1)

首先有一些类包含引用...

public class CellRef : IEquatable<CellRef>
{
    public int Row { get; private set; }
    public int Col { get; private set; }

    // some more code...
}

public class CellRange : IEquatable<CellRange>
{
    public CellRef Start { get; private set; }
    public CellRef Stop { get; private set; }

    // some more code...
}

那么算法和方法...... 在传递给此方法之前,需要将单元格列表放在List中并排序

    public static string GetSimplifiedRangeString(List<CellRef> cellList)
    {
        #region Column wise simplify (identify lines)

        Dictionary<CellRef, CellRef> rowRanges = new Dictionary<CellRef, CellRef>(new CellRefEqualityComparer());

        int currentRangeStart = 0;
        for (int currentRangeStop = 0; currentRangeStop < cellList.Count; currentRangeStop++)
        {
            CellRef currentCell = cellList[currentRangeStop];
            CellRef previousCell = (currentRangeStop == 0) ? null : cellList[currentRangeStop - 1];

            bool cont = IsContigousX(currentCell, previousCell);

            if (!cont)
            {
                currentRangeStart = currentRangeStop;
            }

            if (!rowRanges.ContainsKey(cellList[currentRangeStart]))
                rowRanges.Add(cellList[currentRangeStart], cellList[currentRangeStop]);
            else
                rowRanges[cellList[currentRangeStart]] = cellList[currentRangeStop];
        }

        #endregion


        #region Row wise simplify (identify rectangles)

        List<CellRange> rangeList = new List<CellRange>();
        foreach (KeyValuePair<CellRef, CellRef> range in rowRanges)
        {
            rangeList.Add(new CellRange(range.Key, range.Value));                
        }            
        Dictionary<CellRange, CellRange> colRanges = new Dictionary<CellRange, CellRange>(new CellRangeEqualityComparer()); 

        currentRangeStart = 0;
        for (int currentRangeStop = 0; currentRangeStop < rangeList.Count; currentRangeStop++)
        {
            CellRange currentCellRange = rangeList[currentRangeStop];
            CellRange previousCellRange = (currentRangeStop == 0) ? null : rangeList[currentRangeStop - 1];

            bool cont = IsContigousY(currentCellRange, previousCellRange);

            if (!cont)
            {
                currentRangeStart = currentRangeStop;
            }

            if (!colRanges.ContainsKey(rangeList[currentRangeStart]))
                colRanges.Add(rangeList[currentRangeStart], rangeList[currentRangeStop]);
            else
                colRanges[rangeList[currentRangeStart]] = rangeList[currentRangeStop];
        }

        #endregion


        #region Simplify ranges (identify atomic lines and rectangles)

        StringBuilder retStr = new StringBuilder();
        foreach (KeyValuePair<CellRange, CellRange> ranges in colRanges)
        {
            string rangePart = string.Empty;
            if (ranges.Key.Equals(ranges.Value))
            {
                if (ranges.Key.Start.Equals(ranges.Key.Stop))
                {
                    rangePart = ranges.Key.Start.ToString();
                }
                else
                {
                    rangePart = ranges.Key.ToString();
                }
            }
            else
            {
                rangePart = new CellRange(ranges.Key.Start, ranges.Value.Stop).ToString();
            }

            if (retStr.Length == 0)
            {
                retStr.Append(rangePart);
            }
            else
            {
                retStr.Append("," + rangePart);
            }
        }

        return retStr.ToString();

        #endregion
    }

    /// <summary>
    /// Checks whether the given two cells represent a line.
    /// </summary>
    /// <param name="currentCell">Line start</param>
    /// <param name="previousCell">Line end</param>
    /// <returns></returns>
    private static bool IsContigousX(CellRef currentCell, CellRef previousCell)
    {
        if (previousCell == null)
            return false;
        return (currentCell.Row == previousCell.Row) && (currentCell.Col == (previousCell.Col + 1));
    }

    /// <summary>
    /// Checks whether the given two cells represents a rectangle.
    /// </summary>
    /// <param name="currentCellRange">Top-left cell</param>
    /// <param name="previousCellRange">Bottom-right cell</param>
    /// <returns></returns>
    private static bool IsContigousY(CellRange currentCellRange, CellRange previousCellRange)
    {
        if (previousCellRange == null)
            return false;

        bool sameVertically = (currentCellRange.Start.Col == previousCellRange.Start.Col) && (currentCellRange.Stop.Col == previousCellRange.Stop.Col);
        bool contigous = (currentCellRange.Start.Row == currentCellRange.Stop.Row) && (previousCellRange.Start.Row == previousCellRange.Stop.Row) && ((previousCellRange.Stop.Row + 1) == currentCellRange.Stop.Row);
        return sameVertically && contigous;
    }

希望这有助于某人。

答案 4 :(得分:0)

我认为没有任何方法可以检索长范围的细胞参考。我宁愿选择完整的工作表,然后通过编码进行导航。