Ich möchte ein mehrdimensionales Array formatierter Zellenwerte aus Excel effizient abrufen können. Wenn ich formatierte Werte sage, meine ich, dass ich sie genau so erhalten möchte, wie sie in Excel erscheinen, mit allen Zellen NumberFormat angewendet.Formatierte Zellenwerte effizient abrufen
Die Eigenschaften Range.Value und Range.Value2 eignen sich hervorragend zum Abrufen der Zellenwerte einer großen Anzahl von Zellen in einem mehrdimensionalen Array. Aber das sind die tatsächlichen Zellenwerte (zumindest mit Range.Value2 ist, ich bin mir nicht ganz sicher, was Range.Value in Bezug auf einige der Werte tut).
Wenn ich den tatsächlichen Text abrufen möchte, der in den Zellen angezeigt wird, kann ich die Range.Text-Eigenschaft verwenden. Dies hat einige Vorbehalte. Zuerst müssen Sie die Zellen automatisch anpassen, sonst erhalten Sie etwas wie ####, wenn nicht der gesamte Text mit der aktuellen Zellenbreite sichtbar ist. Zweitens funktioniert Range.Text nicht für mehr als eine Zelle gleichzeitig, sodass Sie alle Zellen im Bereich durchlaufen müssen, was bei großen Datenmengen extrem langsam sein kann. Die andere Methode, die ich ausprobiert habe, ist, den Bereich in die Zwischenablage zu kopieren und dann den Zwischenablage-Text als tabulatorgetrennten Datenstrom zu analysieren und in ein mehrdimensionales Array zu übertragen. Das scheint gut zu funktionieren, obwohl es langsamer ist als Range.Value2 zu bekommen, ist es viel schneller für große Datenmengen als Range.Text. Die Idee, die System-Zwischenablage zu verwenden, gefällt mir jedoch nicht. Wenn dies ein sehr langer Vorgang war, der 60 Sekunden dauert und während dieser Vorgang ausgeführt wird, kann der Benutzer entscheiden, zu einer anderen Anwendung zu wechseln, und wäre sehr unglücklich zu finden, dass seine Zwischenablage entweder nicht funktioniert oder mysteriöse Daten enthält.
Gibt es eine Möglichkeit, die formatierten Zellenwerte effizient zu einem mehrdimensionalen Array abrufen zu können?
Ich habe einen Beispielcode hinzugefügt, der in einer VSTO-App über einige Multifunktionsleisten-Schaltflächen ausgeführt wird. Die erste setzt einige gute Testwerte und Zahlenformate und die zweite Schaltfläche zeigt, wie sie aussehen, wenn sie mit einer dieser Methoden in einer MessageBox abgerufen werden.
Die Beispielausgabe auf meinem System ist (es aufgrund von Ländereinstellungen auf Ihrem unterschiedlich sein könnte):
Output using Range.Value
1/25/2008 3:19:32 PM 5.12345
2008-01-25 15:19:32 0.456
Output using Range.Value2
39472.6385648148 5.12345
2008-01-25 15:19:32 0.456
Output using Clipboard Copy
1/25/2008 15:19 5.12
2008-01-25 15:19:32 45.60%
Output using Range.Text and Autofit
1/25/2008 15:19 5.12
2008-01-25 15:19:32 45.60%
Die Range.Text und Zwischenablage Methoden produzieren die richtige Ausgabe, aber wie oben erklärt haben sie beide Probleme: Range.Text ist langsam und Zwischenablage ist eine schlechte Übung.
private void SetSampleValues()
{
var sheet = (Microsoft.Office.Interop.Excel.Worksheet) Globals.ThisAddIn.Application.ActiveSheet;
sheet.Cells.ClearContents();
sheet.Cells.ClearFormats();
var range = sheet.Range["A1"];
range.NumberFormat = "General";
range.Value2 = "2008-01-25 15:19:32";
range = sheet.Range["A2"];
range.NumberFormat = "@";
range.Value2 = "2008-01-25 15:19:32";
range = sheet.Range["B1"];
range.NumberFormat = "0.00";
range.Value2 = "5.12345";
range = sheet.Range["B2"];
range.NumberFormat = "0.00%";
range.Value2 = ".456";
}
private string ArrayToString(ref object[,] vals)
{
int dim1Start = vals.GetLowerBound(0); //Excel Interop will return index-1 based arrays instead of index-0 based
int dim1End = vals.GetUpperBound(0);
int dim2Start = vals.GetLowerBound(1);
int dim2End = vals.GetUpperBound(1);
var sb = new StringBuilder();
for (int i = dim1Start; i <= dim1End; i++)
{
for (int j = dim2Start; j <= dim2End; j++)
{
sb.Append(vals[i, j]);
if (j != dim2End)
sb.Append("\t");
}
sb.Append("\n");
}
return sb.ToString();
}
private void GetCellValues()
{
var sheet = (Microsoft.Office.Interop.Excel.Worksheet)Globals.ThisAddIn.Application.ActiveSheet;
var usedRange = sheet.UsedRange;
var sb = new StringBuilder();
sb.Append("Output using Range.Value\n");
var vals = (object [,]) usedRange.Value; //1-based array
sb.Append(ArrayToString(ref vals));
sb.Append("\nOutput using Range.Value2\n");
vals = (object[,])usedRange.Value2; //1-based array
sb.Append(ArrayToString(ref vals));
sb.Append("\nOutput using Clipboard Copy\n");
string previousClipboardText = Clipboard.GetText();
usedRange.Copy();
string clipboardText = Clipboard.GetText();
Clipboard.SetText(previousClipboardText);
vals = new object[usedRange.Rows.Count, usedRange.Columns.Count]; //0-based array
ParseClipboard(clipboardText,ref vals);
sb.Append(ArrayToString(ref vals));
sb.Append("\nOutput using Range.Text and Autofit\n");
//if you dont autofit, Range.Text may give you something like #####
usedRange.Columns.AutoFit();
usedRange.Rows.AutoFit();
vals = new object[usedRange.Rows.Count, usedRange.Columns.Count];
int startRow = usedRange.Row;
int endRow = usedRange.Row + usedRange.Rows.Count - 1;
int startCol = usedRange.Column;
int endCol = usedRange.Column + usedRange.Columns.Count - 1;
for (int r = startRow; r <= endRow; r++)
{
for (int c = startCol; c <= endCol; c++)
{
vals[r - startRow, c - startCol] = sheet.Cells[r, c].Text;
}
}
sb.Append(ArrayToString(ref vals));
MessageBox.Show(sb.ToString());
}
//requires reference to Microsoft.VisualBasic to get TextFieldParser
private void ParseClipboard(string text, ref object[,] vals)
{
using (var tabReader = new TextFieldParser(new StringReader(text)))
{
tabReader.SetDelimiters("\t");
tabReader.HasFieldsEnclosedInQuotes = true;
int row = 0;
while (!tabReader.EndOfData)
{
var fields = tabReader.ReadFields();
for (int i = 0; i < fields.Length; i++)
vals[row, i] = fields[i];
row++;
}
}
}
private void button1_Click(object sender, RibbonControlEventArgs e)
{
SetSampleValues();
}
private void button2_Click(object sender, RibbonControlEventArgs e)
{
GetCellValues();
}
Versuchen Sie Folgendes: Exportieren Sie das formatierte Arbeitsblatt in eine CSV-Datei. Erstellen Sie ein neues Arbeitsblatt und ** Importieren ** (nicht öffnen) die CSV-Datei. Wenn Sie dies tun, wird der Textimport-Assistent geöffnet und Sie geben jede Spalte als Text an. Sie können dann den UsedRange dieses neuen Arbeitsblatts in einem einzigen Schritt in ein Varianten-Array einfügen. –
danke, aber ich denke, dass diese Methode ziemlich langsam und fehleranfällig sein wird, da Excel nicht das robusteste Programm für CSVs ist.Ich würde Excel nie vertrauen, um einen csv mit Unicode-Zeichen, führenden Nullen, Daten, Trennzeichen innerhalb des Zellenwerts, Zeilenumbruchzeichen innerhalb des Zellenwerts, usw. – jjdem
zu exportieren Realisierte nicht Ihre Anforderung für Unicode-Zeichen. Ich habe nur die von Ihnen bereitgestellten Beispieldaten verwendet, was gut funktioniert hat. –