Supprimer les lignes vides avec Excel Interop
j'ai des fichiers excel fournis par l'utilisateur qui doivent être convertis en PDF. En utilisant excel interop, je peux le faire avec .ExportAsFixedFormat()
. Mon problème se pose quand un cahier a des millions de lignes. Cela se transforme en un fichier qui a 50k+ pages. Ce serait bien si le cahier de travail avait contenu dans toutes ces lignes. Chaque fois qu'un de ces fichiers apparaît, il y a peut-être 50 lignes qui ont du contenu et le reste est vide. Comment puis-je enlever les lignes vides pour pouvoir les exporter vers une taille décente PDF?
j'ai essayé de commencer à la fin de la rangée et, un par un, en utilisant
CountA
pour vérifier si la ligne a du contenu et si elle en a, supprimez-la. Non seulement cela prend une éternité, mais cela semble échouer Après environ 100k lignes avec l'erreur suivante:impossible d'évaluer l'expression parce que le code est optimisé ou qu'un cadre natif est au sommet de la pile d'appels.
j'ai essayé d'utiliser
SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues)
mais cela comprend une rangée s'il y a la cellule a le formatage (comme une couleur bg).j'ai essayé d'utiliser
Worksheet.UsedRange
et puis en supprimant tout ce qui suit, maisUsedRange
a le même problème que le point 2.
C'est le code que j'ai essayé:
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
rows = sheet.Rows;
currentRowIndex = rows.Count;
bool contentFound = false;
while (!contentFound && currentRowIndex > 0)
{
currentRow = rows[currentRowIndex];
if (Application.WorksheetFunction.CountA(currentRow) == 0)
{
currentRow.Delete();
}
else
{
contentFound = true;
}
Marshal.FinalReleaseComObject(currentRow);
currentRowIndex--;
}
Marshal.FinalReleaseComObject(rows);
Marshal.FinalReleaseComObject(sheet);
}
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
rows = sheet.Rows;
lastCell = rows.SpecialCells(XlCellType.xlCellTypeLastCell, XlSpecialCellsValue.xlTextValues);
int startRow = lastCell.Row;
Range range = sheet.get_Range(lastCell.get_Address(RowAbsolute: startRow));
range.Delete();
Marshal.FinalReleaseComObject(range);
Marshal.FinalReleaseComObject(lastCell);
Marshal.FinalReleaseComObject(rows);
Marshal.FinalReleaseComObject(sheet);
}
j'ai un problème avec mon code, est-ce un problème de l'interopérabilité ou peut-être que c'est juste une limitation de ce que Excel peut faire? Est-il un meilleur moyen de faire ce que j'essaie?
7 réponses
je vous suggère d'obtenir le nombre de lignes qui contiennent certaines valeurs, en utilisant CountA (comme vous avez essayé au point 1). Puis copiez ces lignes dans une nouvelle feuille et exportez-la de là. Il sera plus facile de copier quelques lignes sur une nouvelle feuille et de travailler dessus, plutôt que d'essayer de supprimer un grand nombre de lignes de la feuille source.
pour créer une nouvelle feuille et copier des lignes, vous pouvez utiliser le code suivant:
excel.Worksheet tempSheet = workbook.Worksheets.Add();
tempSheet.Name = sheetName;
workbook.Save();
//créer une nouvelle méthode pour copier de nouveaux rangs
//que l'rowindex, vous pouvez passer le nombre total de lignes que vous avez trouvé l'aide Nbval
public void CopyRows(excel.Workbook workbook, string sourceSheetName, string DestSheetName, int rowIndex)
{
excel.Worksheet sourceSheet = (excel.Worksheet)workbook.Sheets[sourceSheetName];
excel.Range source = (excel.Range)sourceSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow;
excel.Worksheet destSheet = (excel.Worksheet)workbook.Sheets[DestSheetName];
excel.Range dest = (excel.Range)destSheet.Range["A" + rowIndex.ToString(), Type.Missing].EntireRow;
source.Copy(dest);
excel.Range newRow = (excel.Range)destSheet.Rows[rowIndex+1];
newRow.Insert();
workbook.Save();
}
Avez-vous essayé Sheet1.Range("A1").CurrentRegion.ExportAsFixedFormat()
où Sheet1 est un nom de feuille valide et " A1 " est une cellule que vous pouvez tester pour vous assurer qu'elle est située dans la gamme que vous voulez exporter?
la question demeure, pourquoi Excel pense - t-il qu'il y a des données dans ces cellules "vides"? Mise en forme? Une zone d'impression préexistante qui doit être nettoyée? Je sais que j'ai déjà rencontré des situations comme ça, ce sont les seules possibilités qui me viennent à l'esprit en ce moment.
Essayez ces étapes -
- copier
Worksheet.UsedRange
pour une feuille séparée (feuil2). - utilisez la pâte spéciale de sorte que le formatage est retenu
- essayez d'analyser sheet2 pour les lignes inutilisées
si cela n'aide pas à répéter l'étape 2 avec les informations de formatage étant effacées et puis parsing sheet2. vous pouvez toujours copier les informations de format plus tard (si elles sont assez simples)
si vous pouvez d'abord charger le fichier Excel dans un ensemble de données via L'OleDBAdapter, il est relativement facile de supprimer les lignes vides sur l'importation... Essayez ceci Oldbadapter Excel QA j'ai posté via stack overflow.
puis exportez L'ensemble de données vers un nouveau fichier Excel et convertissez ce fichier en PDF. Cela peut être un grand " si " bien que bien sûr en fonction de la mise en page excel (ou l'absence là de).
j'ai eu à résoudre ce problème, aujourd'hui, pour ce qui pourrait être un sous-ensemble des cas possibles.
si votre feuille de calcul remplit les conditions suivantes:
- toutes les colonnes avec des données ont le texte d'en-tête dans la ligne 1.
- toutes les lignes avec des données sont dans l'ordre jusqu'à la première ligne vide.
Ensuite, le code suivant peut aider:
private static string[,] LoadCellData(Excel.Application excel, dynamic sheet)
{
int countCols = CountColsToFirstBlank(excel, sheet);
int countRows = CountRowsToFirstBlank(excel, sheet);
cellData = new string[countCols, countRows];
string datum;
for (int i = 0; i < countCols; i++)
{
for (int j = 0; j < countRows; j++)
{
try
{
if (null != sheet.Cells[i + 1, j + 1].Value)
{
datum = excel.Cells[i + 1, j + 1].Value.ToString();
cellData[i, j] = datum;
}
}
catch (Exception ex)
{
lastException = ex;
//Console.WriteLine(String.Format("LoadCellData [{1}, {2}] reported an error: [{0}]", ex.Message, i, j));
}
}
}
return cellData;
}
private static int CountRowsToFirstBlank(Excel.Application excel, dynamic sheet)
{
int count = 0;
for (int j = 0; j < sheet.UsedRange.Rows.Count; j++)
{
if (IsBlankRow(excel, sheet, j + 1))
break;
count++;
}
return count;
}
private static int CountColsToFirstBlank(Excel.Application excel, dynamic sheet)
{
int count = 0;
for (int i = 0; i < sheet.UsedRange.Columns.Count; i++)
{
if (IsBlankCol(excel, sheet, i + 1))
break;
count++;
}
return count;
}
private static bool IsBlankCol(Excel.Application excel, dynamic sheet, int col)
{
for (int i = 0; i < sheet.UsedRange.Rows.Count; i++)
{
if (null != sheet.Cells[i + 1, col].Value)
{
return false;
}
}
return true;
}
private static bool IsBlankRow(Excel.Application excel, dynamic sheet, int row)
{
for (int i = 0; i < sheet.UsedRange.Columns.Count; i++)
{
if (null != sheet.Cells[i + 1, row].Value)
{
return false;
}
}
return true;
}
Pouvez-vous essayer avec le code ci-dessous :
for (int rowIndex = workSheet.Dimension.Start.Row; rowIndex <= workSheet.Dimension.End.Row; rowIndex++)
{
//Assume the first row is the header. Then use the column match ups by name to determine the index.
//This will allow you to have the order of the header.Keys change without any affect.
var row = workSheet.Cells[string.Format("{0}:{0}", rowIndex)];
// check if the row and column cells are empty
bool allEmpty = row.All(c => string.IsNullOrWhiteSpace(c.Text));
if (allEmpty)
continue; // skip this row
else{
//here read header
if()
{
//some code
}
else
{
//some code to read body
}
}
}
J'espère que cette aide, sinon laissez-moi savoir si vous avez besoin d'une description sur le code.
mise à Jour :
- code ci-dessous est utilisé pour vérifier le nombre de lignes dans la feuille de calcul. une boucle pour traverse jusqu'à la fin de la ligne de la feuille de travail.
for (int rowIndex = feuille de calcul.Dimension.Commencer.Ligne; rowIndex < = feuille de travail.Dimension.Fin.Row; rowIndex++)
- ici, nous vérifient si les cellules de la ligne et de la colonne sont vides en utilisant linq:
bool allEmpty = row.Tous les(c => string.IsNullOrWhiteSpace(C. Text));
si (allEmpty)
continue; // si la valeur est true alors sauter cette ligne
autre // lire les en-têtes (en supposant qu'ils soient présentés dans la feuille de travail)
// lire les données de la ligne Sage et ensuite faire les étapes nécessaires.
en espérant que cela efface maintenant.
essayez le code suivant:
for (int i = 0; i < worksheets.Count; i++)
{
sheet = worksheets[i + 1];
sheet.Columns("A:A").SpecialCells(XlCellType.xlCellTypeBlanks).EntireRow.Delete
sheet.Rows("1:1").SpecialCells(XlCellType.xlCellTypeBlanks).EntireColumn.Delete
Marshal.FinalReleaseComObject(sheet);
}