将Excel范围转换为C#数组

11

我想用这段代码将 Excel 范围转换为 C# 数组:

System.Array MyRange = (System.Array)range.cells.value;

for (int k = 0; k <= MyRange.Length; k++)
{
    List<service_name> _ml = new List<service_name>();
    for (int j = 1; j < dataitems.Count; j++)
    {
        // enter code here
    }
}

然后像上面的循环一样迭代它。

但是这段代码不起作用,并抛出此异常:

"Unable to cast object of type 'System.String' to type 'System.Array'."


2
它们应该像Range.Cells.Value一样大写。但是关于您的问题没有足够的信息,请澄清一下。 - Soner Gönül
6个回答

8
根据 Microsoft 在此提供的帮助,我将介绍如何在 Excel 中读取和写入数组。请参见此处了解更多信息。
var xlApp=new Microsoft.Office.Interop.Excel.Application();
var wb=xlApp.Workbooks.Open(fn, ReadOnly: false);
xlApp.Visible=true;
var ws=wb.Worksheets[1] as Worksheet;
var r=ws.Range["A2"].Resize[100, 1];
var array=r.Value;
// array is object[1..100,1..1]
for(int i=1; i<=100; i++)
{
    var text=array[i, 1] as string;
    Debug.Print(text);
}
// to create an [1..100,1..1] array use
var array2=Array.CreateInstance(
    typeof(object), 
    new int[] {100, 1}, 
    new int[] {1, 1}) as object[,];

// fill array2
for(int i=1; i<=100; i++)
{
    array2[i, 1] = string.Format("Text{0}",i);
}
r.Value2=array2;

wb.Close(SaveChanges: true);
xlApp.Quit();

Debug Excel


1
@SHINJaeGuk - 从这里开始:https://learn.microsoft.com/zh-cn/dotnet/csharp/programming-guide/interop/how-to-access-office-onterop-objects - John Alexiou

6

原帖中的错误信息是在范围只包含一个单元格时出现的,因为结果值的类型是variant,并且实际上可以是数组、双精度浮点数、字符串、日期和null。

一种解决方法是检查单元格计数,并在仅有一个单元格的情况下采取不同的操作。

我的解决方案是创建一个单元格数组。这个方法即使其中一个或多个单元格为空,也能够避免空对象的出现。(当范围内所有单元格都为空时,Range.Cells.Value为null。)

System.Array cellArray = range.Cells.Cast<Excel.Range>().ToArray<Excel.Range>();

如果您更喜欢列表而不是数组(像我一样),您可以使用此代码:
List<Excel.Range> listOfCells = range.Cells.Cast<Excel.Range>().ToList<Excel.Range>();

范围可以是一维或二维的。

最后,如果您确实需要一个字符串数组,这里是它:

string[] strArray = range.Cells.Cast<Excel.Range>().Select(Selector).ToArray<string>();

选择器函数看起来像这样:
public string Selector(Excel.Range cell)
{
    if (cell.Value2 == null)
        return "";
    if (cell.Value2.GetType().ToString() == "System.Double")
        return ((double)cell.Value2).ToString();
    else if (cell.Value2.GetType().ToString() == "System.String")
        return ((string)cell.Value2);
    else if (cell.Value2.GetType().ToString() == "System.Boolean")
        return ((bool)cell.Value2).ToString();
    else
        return "unknown";
}

我在这个函数中加入了"unknown",以防我忘记Value2可能返回的所有数据类型。如果你发现其他类型,请改进此函数。


2

注意:此示例仅适用于包含多个单元格的范围。如果范围只有一个单元格(1x1),Excel将以特殊方式处理它,且range.Value2不会返回二维数组,而是单个值。这些特殊情况会让你感到非常困惑,还有零和非零数组下限:

原始答案:最初的回答

using Excel = Microsoft.Office.Interop.Excel;

private static void Test()
{
    Excel.Range range = Application.ActiveWorkbook.ActiveSheet.Range["A1:B2"]; // 2x2 array

    range.Cells[1, 2] = "Foo"; // Sets Cell A2 to "Foo"
    dynamic[,] excelArray = range.Value2 as dynamic[,]; // This is a very fast operation
    Console.Out.WriteLine(excelArray[1, 2]); // => Foo

    excelArray[1, 2] = "Bar";
    range.Value2 = excelArray; // Sets Cell A2 to "Bar", again a fast operation even for large arrays
    Console.Out.WriteLine(range.Cells[1, 2]); // => Bar

请注意,excelArray的行和列下限为1:"最初的回答"
    Console.Out.WriteLine("RowLB: " + excelArray.GetLowerBound(0)); // => RowLB: 1
    Console.Out.WriteLine("ColLB: " + excelArray.GetLowerBound(1)); // => ColLB: 1

但是,如果你在C#中声明一个newArray并进行赋值,那么下限将为0,但它仍然可以正常工作:

最初的回答

    dynamic[,] newArray = new dynamic[2, 2]; // Same dimensions as "A1:B2" (2x2)
    newArray[0, 1] = "Foobar";
    range.Value2 = newArray; // Sets Cell A2 to "Foobar"
    Console.Out.WriteLine(range.Cells[1, 2]); // => Foobar

将该值从范围中提取出来,将给您带来原始数组,其下限为0:
    range.Cells[1, 2] = "Fubar";

    dynamic[,] lastArray = range.Value2 as dynamic[,];
    Console.Out.WriteLine(lastArray[0, 1]); // => Fubar

    Console.Out.WriteLine("RowLB: " + lastArray.GetLowerBound(0)); // => RowLB: 0
    Console.Out.WriteLine("ColLB: " + lastArray.GetLowerBound(1)); // => ColLB: 0
}

使用Excel Interop可能会让人望而生畏,因为代码库中存在许多特殊情况,但我希望这可以至少澄清其中一个。

"Original Answer"翻译成中文是"最初的回答"。


1
晚来一步,但这里有一个方法可以做到:
static string[][] GetStringArray(Object rangeValues)
{
    string[][] stringArray = null;

    Array array = rangeValues as Array;
    if (null != array)
    {
        int rank = array.Rank;
        if (rank > 1)
        {
            int rowCount = array.GetLength(0);
            int columnCount = array.GetUpperBound(1);

            stringArray = new string[rowCount][];

            for (int index = 0; index < rowCount; index++)
            {
                stringArray[index] = new string[columnCount];

                for (int index2 = 0; index2 < columnCount; index2++)
                {
                    Object obj = array.GetValue(index + 1, index2 + 1);
                    if (null != obj)
                    {
                        string value = obj.ToString();

                        stringArray[index][index2] = value;
                    }
                }
            }
        }
    }

    return stringArray;
}

被调用:

string[][] rows = GetStringArray(range.Cells.Value2);

1
stringArray[index] = new string[columnCount-1]; 是错误的,会导致索引超出范围。正确的写法是 stringArray[index] = new string[columnCount];。 - Lévay Lőrinc
@LévayLőrinc - 感谢您发现这个问题。我已经相应地更新了示例代码。 - James John McGuire 'Jahmic'

1
错误意味着该值是一个字符串,因此您无法直接将其转换为数组。
例如,如果该值是逗号分隔的字符串,则可以使用Split函数获取一个数组:
string[] MyRange = (range.Cells.Value + "").Split(',');
for (int k = 0; k < MyRange.Length; k++)
{
    //...loop here...
}

同时修复了您的循环,因为您将会得到“索引超出范围”错误。


0
问题出在当你的区域只有一个单元格时,“range.value”/“range.cell.value” 是字符串类型的值,这个字符串无法放入对象数组中。因此,请检查你的区域是否只有一个单元格或更多,并根据情况进行操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接