it-swarm.com.de

Holen Sie sich mit Interop den letzten nicht leeren Spalten- und Zeilenindex aus Excel

Ich versuche, alle zusätzlichen leeren Zeilen und Spalten mit Interop Library aus einer Excel-Datei zu entfernen.

Ich folgte dieser Frage Schnellste Methode zum Entfernen leerer Zeilen und Spalten aus Excel-Dateien mit Interop und ich finde es hilfreich. 

Aber ich habe Excel-Dateien, die eine kleine Menge von Daten enthalten, aber viele leere Zeilen und Spalten (von der letzten nicht leeren Zeile (oder Spalte) bis zum Ende des Arbeitsblatts)

Ich habe versucht, über Zeilen und Spalten zu schleifen, aber die Schleife dauert Stunden.

Ich versuche, den letzten nicht leeren Zeilen- und Spaltenindex abzurufen, damit ich den gesamten leeren Bereich in einer Zeile löschen kann 

XlWks.Range("...").EntireRow.Delete(xlShiftUp)

 enter image description here

Hinweis: Ich versuche, die letzte Zeile mit Daten abzurufen, um alle zusätzlichen Leerzeichen zu entfernen (nach dieser Zeile oder Spalte).

Irgendwelche Vorschläge?

29
Yahfoufi

Update 1

Wenn Sie die Excel-Daten mit c # importieren möchten, vorausgesetzt, Sie haben den höchsten verwendeten Index in Ihrem Arbeitsblatt ermittelt (in dem von Ihnen geposteten Bild ist es Col = 10, Row = 16) maximal verwendete Indizes zu Buchstaben, so dass es J16 ist und nur den verwendeten Bereich mit und OLEDBCommand auswählen

SELECT * FROM [Sheet1$A1:J16]

Sonst glaube ich nicht, dass es einfach ist, eine schnellere Methode zu finden.

Sie können auf diese Artikel verweisen, um Indizes in Alphabet zu konvertieren und mithilfe von OLEDB eine Verbindung zu Excel herzustellen:


Erste Antwort

Wie Sie sagten, gingen Sie von folgender Frage aus:

Und Sie versuchen, "die letzte Zeile mit Daten zu erhalten, um alle zusätzlichen Leerzeichen (nach dieser Zeile oder Spalte) zu entfernen"

Angenommen, Sie arbeiten mit der akzeptierten Antwort (bereitgestellt von @JohnG ), so dass Sie eine Zeile Code hinzufügen können, um die zuletzt verwendete Zeile und Spalte abzurufen

Leere Zeilen werden in einer Liste mit ganzen Zahlen gespeichert. rowsToDelete

Mit dem folgenden Code können Sie die letzten nicht leeren Zeilen abrufen, deren Index kleiner als die letzte leere Zeile ist

List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();

Und wenn NonEmptyRows.Max() < rowsToDelete.Max() die letzte nicht leere Zeile NonEmptyRows.Max() ist, ist dies worksheet.Rows.Count und es gibt keine leeren Zeilen nach der zuletzt verwendeten Zeile.

Dasselbe kann gemacht werden, um die letzte nicht leere Spalte zu erhalten.

Der Code wird in den Funktionen DeleteCols und DeleteRows bearbeitet:

    private static void DeleteRows(List<int> rowsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
    {
        // the rows are sorted high to low - so index's wont shift

        List<int> NonEmptyRows = Enumerable.Range(1, rowsToDelete.Max()).ToList().Except(rowsToDelete).ToList();

        if (NonEmptyRows.Max() < rowsToDelete.Max())
        {

            // there are empty rows after the last non empty row

            Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[NonEmptyRows.Max() + 1,1];
            Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[rowsToDelete.Max(), 1];

            //Delete all empty rows after the last used row
            worksheet.Range[cell1, cell2].EntireRow.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftUp);


        }    //else last non empty row = worksheet.Rows.Count



        foreach (int rowIndex in rowsToDelete.Where(x => x < NonEmptyRows.Max()))
        {
            worksheet.Rows[rowIndex].Delete();
        }
    }

    private static void DeleteCols(List<int> colsToDelete, Microsoft.Office.Interop.Excel.Worksheet worksheet)
    {
        // the cols are sorted high to low - so index's wont shift

        //Get non Empty Cols
        List<int> NonEmptyCols = Enumerable.Range(1, colsToDelete.Max()).ToList().Except(colsToDelete).ToList();

        if (NonEmptyCols.Max() < colsToDelete.Max())
        {

            // there are empty rows after the last non empty row

            Microsoft.Office.Interop.Excel.Range cell1 = worksheet.Cells[1,NonEmptyCols.Max() + 1];
            Microsoft.Office.Interop.Excel.Range cell2 = worksheet.Cells[1,NonEmptyCols.Max()];

            //Delete all empty rows after the last used row
            worksheet.Range[cell1, cell2].EntireColumn.Delete(Microsoft.Office.Interop.Excel.XlDeleteShiftDirection.xlShiftToLeft);


        }            //else last non empty column = worksheet.Columns.Count

        foreach (int colIndex in colsToDelete.Where(x => x < NonEmptyCols.Max()))
        {
            worksheet.Columns[colIndex].Delete();
        }
    }
12
Hadi

Vor einigen Jahren habe ich ein MSDN-Codebeispiel erstellt, mit dem ein Entwickler die zuletzt verwendete Zeile und Spalte aus einem Arbeitsblatt abrufen kann. Ich habe es geändert und den gesamten erforderlichen Code in eine Klassenbibliothek mit einem Front-End für Windows-Formulare gestellt, um die Operation zu demonstrieren.

Der zugrunde liegende Code verwendet Microsoft.Office.Interop.Excel.

Speicherort auf einem Laufwerk von Microsoft https://1drv.ms/u/s!AtGAgKKpqdWjiEGdBzWDCSCZAMaMM

Hier bekomme ich das erste Blatt in einer Excel-Datei, bekomme die zuletzt verwendete Zeile und Spalte und präsentiere sie als gültige Zellenadresse.

Private Sub cmdAddress1_Click(sender As Object, e As EventArgs) Handles cmdAddress1.Click
    Dim ops As New GetExcelColumnLastRowInformation
    Dim info = New UsedInformation
    ExcelInformationData = info.UsedInformation(FileName, ops.GetSheets(FileName))

    Dim SheetName As String = ExcelInformationData.FirstOrDefault.SheetName

    Dim cellAddress = (
        From item In ExcelInformationData
        Where item.SheetName = ExcelInformationData.FirstOrDefault.SheetName
        Select item.LastCell).FirstOrDefault

    MessageBox.Show($"{SheetName} - {cellAddress}")

End Sub

Innerhalb des Demo-Projekts bekomme ich auch alle Blätter für eine Excel-Datei, präsentiere sie in einer ListBox. Wählen Sie aus dem Listenfeld einen Blattnamen aus, und rufen Sie die letzte Zeile und Spalte dieses Blattes in einer gültigen Zellenadresse auf.

Private Sub cmdAddress_Click(sender As Object, e As EventArgs) Handles cmdAddress.Click
    Dim cellAddress =
        (
            From item In ExcelInformationData
            Where item.SheetName = ListBox1.Text
            Select item.LastCell).FirstOrDefault

    If cellAddress IsNot Nothing Then
        MessageBox.Show($"{ListBox1.Text} {cellAddress}")
    End If

End Sub

Auf den ersten Blick beim Öffnen der Lösung über den Link oben werden Sie feststellen, dass sehr viel Code vorhanden ist. Der Code ist optimal und gibt alle Objekte sofort frei.

8
Karen Payne

Ich verwende ClosedXml, das nützliche Methoden "LastUsedRow" und "LastUsedColumn" enthält.

var wb = new XLWorkbook(@"<path>\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");

for (int i = sheet.LastRowUsed().RowNumber() - 1; i >= 1; i--)
{
    var row = sheet.Row(i);
    if (row.IsEmpty())
    {
        row.Delete();
    }
}

wb.Save();

Diese einfache Schleife löschte 5000 von 10.000 Zeilen in 38 Sekunden. Nicht schnell, aber viel besser als Stunden. Das hängt davon ab, mit wie vielen Zeilen/Spalten Sie sich natürlich auseinandersetzen, was Sie nicht sagen .. __ Nach weiteren Tests mit 25000 leeren Zeilen von 50000 dauert es jedoch etwa 30 Minuten, um die leeren Zeilen in a zu löschen Schleife. Das Löschen von Zeilen ist kein effizienter Prozess.

Eine bessere Lösung ist, ein neues Blatt zu erstellen und dann die Zeilen zu kopieren, die Sie behalten möchten.

Schritt 1 - Erstellen Sie ein Blatt mit 50000 Zeilen und 20 Spalten. Jede zweite Zeile und Spalte ist leer.

var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx");
var sheet = wb.Worksheet("Sheet1");
sheet.Clear();

for (int i = 1; i < 50000; i+=2)
{
    var row = sheet.Row(i);

    for (int j = 1; j < 20; j += 2)
    {
        row.Cell(j).Value = i * j;
    }
}

Schritt 2 - Kopieren Sie die Zeilen mit Daten auf ein neues Blatt. Dies dauert 10 Sekunden.

var wb = new XLWorkbook(@"C:\Users\passp\Documents\test.xlsx", XLEventTracking.Disabled);
var sheet = wb.Worksheet("Sheet1");

var sheet2 = wb.Worksheet("Sheet2");
sheet2.Clear();

sheet.RowsUsed()
    .Where(r => !r.IsEmpty())
    .Select((r, index) => new { Row = r, Index = index + 1} )
    .ForEach(r =>
    {
        var newRow = sheet2.Row(r.Index);

        r.Row.CopyTo(newRow);
    }
);

wb.Save();

Schritt 3 - dies wäre der gleiche Vorgang für die Spalten.

7
Phil
  • Um den letzten nicht leeren Spalten-/Zeilenindex zu erhalten, kann die Excel-Funktion Find verwendet werden. Siehe GetLastIndexOfNonEmptyCell.
  • Dann wird die Excel-ArbeitsblattfunktionCountA verwendet, um festzustellen, ob die Zellen leer sind, und union die gesamten Zeilen/Spalten zu einem Zeilen-/Spaltenbereich. 
  • Diese Bereiche werden sofort gelöscht.

public void Yahfoufi(string excelFile)
{
    var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
    var wrb = exapp.Workbooks.Open(excelFile);
    var sh = wrb.Sheets["Sheet1"];
    var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
    var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);
    var target = sh.Range[sh.Range["A1"], sh.Cells[lastRow, lastCol]];
    Range deleteRows = GetEmptyRows(exapp, target);
    Range deleteColumns = GetEmptyColumns(exapp, target);
    deleteColumns?.Delete();
    deleteRows?.Delete();
}

private static int GetLastIndexOfNonEmptyCell(
    Microsoft.Office.Interop.Excel.Application app,
    Worksheet sheet,
    XlSearchOrder searchOrder)
{
    Range rng = sheet.Cells.Find(
        What: "*",
        After: sheet.Range["A1"],
        LookIn: XlFindLookIn.xlFormulas,
        LookAt: XlLookAt.xlPart,
        SearchOrder: searchOrder,
        SearchDirection: XlSearchDirection.xlPrevious,
        MatchCase: false);
    if (rng == null)
        return 1;
    return searchOrder == XlSearchOrder.xlByRows
        ? rng.Row
        : rng.Column;
}

private static Range GetEmptyRows(
    Microsoft.Office.Interop.Excel.Application app,
    Range target)
{
    Range result = null;
    foreach (Range r in target.Rows)
    {
        if (app.WorksheetFunction.CountA(r.Cells) >= 1)
            continue;
        result = result == null
            ? r.EntireRow
            : app.Union(result, r.EntireRow);
    }
    return result;
}

private static Range GetEmptyColumns(
    Microsoft.Office.Interop.Excel.Application app,
    Range target)
{
    Range result = null;
    foreach (Range c in target.Columns)
    {
        if (app.WorksheetFunction.CountA(c.Cells) >= 1)
            continue;
        result = result == null
            ? c.EntireColumn
            : app.Union(result, c.EntireColumn);
    }
    return result;
}

Die zwei Funktionen zum Abrufen leerer Zeilen-/Spaltenbereiche können zu einer Funktion umgestaltet werden, etwa wie folgt:

private static Range GetEntireEmptyRowsOrColumns(
    Microsoft.Office.Interop.Excel.Application app,
    Range target,
    Func<Range, Range> rowsOrColumns,
    Func<Range, Range> entireRowOrColumn)
{
    Range result = null;
    foreach (Range c in rowsOrColumns(target))
    {
        if (app.WorksheetFunction.CountA(c.Cells) >= 1)
            continue;
        result = result == null
            ? entireRowOrColumn(c)
            : app.Union(result, entireRowOrColumn(c));
    }
    return result;
}

Und dann nennen Sie es einfach:

Range deleteColumns = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Columns), (Func<Range, Range>)(r2 => r2.EntireColumn));
Range deleteRows = GetEntireEmptyRowsOrColumns(exapp, target, (Func<Range, Range>)(r1 => r1.Rows), (Func<Range, Range>)(r2 => r2.EntireRow));
deleteColumns?.Delete();
deleteRows?.Delete();

Hinweis: Weitere Informationen finden Sie z. auf diese SO Frage .

Bearbeiten

Versuchen Sie, einfach den Inhalt aller Zellen zu löschen, die nach der zuletzt verwendeten Zelle liegen. 

public void Yahfoufi(string excelFile)
{
    var exapp = new Microsoft.Office.Interop.Excel.Application {Visible = true};
    var wrb = exapp.Workbooks.Open(excelFile);
    var sh = wrb.Sheets["Sheet1"];
    var lastRow = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByRows);
    var lastCol = GetLastIndexOfNonEmptyCell(exapp, sh, XlSearchOrder.xlByColumns);

    // Clear the columns
    sh.Range(sh.Cells(1, lastCol + 1), sh.Cells(1, Columns.Count)).EntireColumn.Clear();

    // Clear the remaining cells
    sh.Range(sh.Cells(lastRow + 1, 1), sh.Cells(Rows.Count, lastCol)).Clear();

}
7
dee

Nehmen wir an, die letzte Eckzelle mit Daten ist J16 - also keine Daten in den Spalten K oder in Zeilen 17 nach unten. Warum löschen Sie sie eigentlich? Was ist das Szenario und was wollen Sie erreichen? Löschen Sie unsere Formatierung? Wird das Löschen unserer Formeln gelöscht, die eine leere Zeichenfolge zeigen? 

In jedem Fall ist das Schleifen nicht der richtige Weg. 

Der folgende Code zeigt eine Möglichkeit, die Clear () - Methode des Range-Objekts zu verwenden, um alle Inhalte, Formeln und Formatierungen aus einem Bereich zu löschen. Wenn Sie sie wirklich löschen möchten, können Sie die Delete () - Methode verwenden, um einen ganzen rechteckigen Bereich mit einem Treffer zu löschen. Wird viel schneller sein als Schleifen ...

//code uses variables declared appropriately as Excel.Range & Excel.Worksheet Using Interop library
int x;
int y;
// get the row of the last value content row-wise
oRange = oSheet.Cells.Find(What: "*", 
                           After: oSheet.get_Range("A1"),
                           LookIn: XlFindLookIn.xlValues,
                           LookAt: XlLookAt.xlPart, 
                           SearchDirection: XlSearchDirection.xlPrevious,
                           SearchOrder: XlSearchOrder.xlByRows);

if (oRange == null)
{
    return;
}
x = oRange.Row;

// get the column of the last value content column-wise
oRange = oSheet.Cells.Find(What: "*",
                           After: oSheet.get_Range("A1"),
                           LookIn: XlFindLookIn.xlValues, LookAt: XlLookAt.xlPart,
                           SearchDirection: XlSearchDirection.xlPrevious,
                           SearchOrder: XlSearchOrder.xlByColumns);
y = oRange.Column;

// now we have the corner (x, y), we can delete or clear all content to the right and below
// say J16 is the cell, so x = 16, and j=10

Excel.Range clearRange;

//set clearRange to ("K1:XFD1048576")
clearRange = oSheet.Range[oSheet.Cells[1, y + 1], oSheet.Cells[oSheet.Rows.Count, oSheet.Columns.Count]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete(); if you REALLY want to hard delete the rows

//set clearRange to ("A17:J1048576")            
clearRange = oSheet.Range[oSheet.Cells[x + 1, 1], oSheet.Cells[oSheet.Rows.Count, y]];
clearRange.Clear(); //clears all content, formulas and formatting
//clearRange.Delete();  if you REALLY want to hard delete the columns
4
MacroMarc

Sie sollten in der Lage sein, die letzte nicht leere Zeile und Spalte mit einem ähnlichen Ergebnis zu finden:

with m_XlWrkSheet
lastRow = .UsedRange.Rows.Count
lastCol = .UsedRange.Columns.Count
end with

Das ist VB.NET, aber es sollte mehr oder weniger funktionieren. Daraufhin werden Zeile 16 und Spalte 10 zurückgegeben (basierend auf Ihrem Bild oben). Dann können Sie damit den Bereich finden, den Sie alle in einer Zeile löschen möchten.

2
garroad_ran

Ich denke, Sie können versuchen, den Bereich zu verwenden.

        Application Excel = new Application();
        Workbook workBook=  Excel.Workbooks.Open("file.xlsx")
        Worksheet excelSheet = workBook.ActiveSheet;
        Range excelRange = excelSheet.UsedRange.Columns[1, Missing.Value] as Range;

        var lastNonEmptyRow = excelRange.Cells.Count;

Der obige Code funktioniert für mich.

1
Shajin Chandran

Scheint, dass Ihr Problem von Microsoft behoben wurde. Werfen Sie einen Blick auf Range.CurrentRegion-Eigenschaft , die einen Bereich zurückgibt, der durch eine Kombination aus leeren Zeilen und leeren Spalten begrenzt ist. Es gibt eine Unannehmlichkeit: diese Eigenschaft kann nicht in einem geschützten Arbeitsblatt verwendet werden.

Weitere Informationen finden Sie unter: So suchen Sie die aktuelle Region, den verwendeten Bereich, die letzte Zeile und die letzte Spalte in Excel mit VBA-Makro

Einige SO -Mitglieder haben etwa die UsedRange-Eigenschaft erwähnt, die ebenfalls nützlich sein könnte, der Unterschied zu CurrentRegion besteht jedoch darin, dass UsedRange einen Bereich zurückgibt, der alle Zellen enthält, die jemals verwendet wurden.
Wenn Sie also eine LAST(row) und LAST(column) mit Daten belegen möchten, müssen Sie End property mit XlDirection: xlToLeft und/oder xlUp verwenden. 

Anmerkung 1:
Wenn Ihre Daten in einem Tabellenformat vorliegen, können Sie die letzte Zelle einfach mit folgendem Befehl suchen:

lastCell = yourWorkseet.UsedRange.End(xlUp)
firstEmtyRow = lastCell.Offset(RowOffset:=1).EntireRow

Anmerkung 2:
Wenn Ihre Daten nicht in einem Tabellenformat sind, müssen Sie die Auflistung der Zeilen und Spalten durchlaufen, um die letzte nicht leere Zelle zu finden.

Viel Glück!

1
Maciej Los