Excelはnullのセルの値を格納しないため、これは理にかなっています。Open XML SDK 2.0生産性ツールを使用してファイルを開き、XMLをセルレベルまでトラバースすると、データを持つセルのみがそのファイルに含まれることがわかります。
オプションは、トラバースするセルの範囲に空白のデータを挿入するか、プログラムでセルがスキップされたことを確認して、インデックスを適切に調整することです。
セル参照A1とC1に文字列を含むExcelドキュメントの例を作成しました。次に、Open XML Productivity ToolでExcelドキュメントを開きました。保存されたXMLは、次のとおりです。
<x:row r="1" spans="1:3"
xmlns:x="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<x:c r="A1" t="s">
<x:v>0</x:v>
</x:c>
<x:c r="C1" t="s">
<x:v>1</x:v>
</x:c>
</x:row>
ここでは、データが最初の行に対応し、その行に2セル分のデータのみが保存されていることがわかります。保存されるデータはA1とC1に対応し、null値のセルは保存されません。
必要な機能を取得するには、上記のようにセルをトラバースできますが、セルが参照している値を確認し、スキップされたセルがあるかどうかを判断する必要があります。これを行うには、セル参照から列名を取得し、その列名をゼロベースのインデックスに変換するための2つのユーティリティ関数が必要になります。
private static List<char> Letters = new List<char>() { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ' };
/// <summary>
/// Given a cell name, parses the specified cell to get the column name.
/// </summary>
/// <param name="cellReference">Address of the cell (ie. B2)</param>
/// <returns>Column Name (ie. B)</returns>
public static string GetColumnName(string cellReference)
{
// Create a regular expression to match the column name portion of the cell name.
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellReference);
return match.Value;
}
/// <summary>
/// Given just the column name (no row index), it will return the zero based column index.
/// Note: This method will only handle columns with a length of up to two (ie. A to Z and AA to ZZ).
/// A length of three can be implemented when needed.
/// </summary>
/// <param name="columnName">Column Name (ie. A or AB)</param>
/// <returns>Zero based index if the conversion was successful; otherwise null</returns>
public static int? GetColumnIndexFromName(string columnName)
{
int? columnIndex = null;
string[] colLetters = Regex.Split(columnName, "([A-Z]+)");
colLetters = colLetters.Where(s => !string.IsNullOrEmpty(s)).ToArray();
if (colLetters.Count() <= 2)
{
int index = 0;
foreach (string col in colLetters)
{
List<char> col1 = colLetters.ElementAt(index).ToCharArray().ToList();
int? indexValue = Letters.IndexOf(col1.ElementAt(index));
if (indexValue != -1)
{
// The first letter of a two digit column needs some extra calculations
if (index == 0 && colLetters.Count() == 2)
{
columnIndex = columnIndex == null ? (indexValue + 1) * 26 : columnIndex + ((indexValue + 1) * 26);
}
else
{
columnIndex = columnIndex == null ? indexValue : columnIndex + indexValue;
}
}
index++;
}
}
return columnIndex;
}
次に、セルを反復処理して、セル参照がcolumnIndexと比較されているかどうかを確認できます。それよりも小さい場合は、tempRowに空白のデータを追加します。それ以外の場合は、セルに含まれている値を読み込みます。(注:以下のコードはテストしていませんが、一般的な考え方が役立つはずです):
DataRow tempRow = dt.NewRow();
int columnIndex = 0;
foreach (Cell cell in row.Descendants<Cell>())
{
// Gets the column index of the cell with data
int cellColumnIndex = (int)GetColumnIndexFromName(GetColumnName(cell.CellReference));
if (columnIndex < cellColumnIndex)
{
do
{
tempRow[columnIndex] = //Insert blank data here;
columnIndex++;
}
while(columnIndex < cellColumnIndex);
}
tempRow[columnIndex] = GetCellValue(spreadSheetDocument, cell);
if (tempRow[i].ToString().IndexOf("Latency issues in") > -1)
{
Console.Write(tempRow[i].ToString());
}
columnIndex++;
}