sstrecord.cs
来自「Excel的操作,其中可以读取及写入Excel 文件」· CS 代码 · 共 359 行
CS
359 行
using System;
using Microsoft.Fawvw.Components.NExcel.ExcelCommon;
using Microsoft.Fawvw.Components.NExcel.Biff;
namespace Microsoft.Fawvw.Components.NExcel.Read.Biff
{
/// <summary> Holds all the strings in the shared string table</summary>
public class SSTRecord:RecordData
{
/// <summary> The total number of strings in this table</summary>
private int totalStrings;
/// <summary> The number of unique strings</summary>
private int uniqueStrings;
/// <summary> The shared strings</summary>
private string[] strings;
/// <summary> The array of continuation breaks</summary>
private int[] continuationBreaks;
/// <summary> A holder for a byte array</summary>
private class ByteArrayHolder
{
/// <summary> the byte holder</summary>
public sbyte[] bytes;
}
/// <summary> A holder for a boolean</summary>
private class BooleanHolder
{
/// <summary> the holder holder</summary>
public bool Value;
}
/// <summary> Constructs this object from the raw data
///
/// </summary>
/// <param name="t">the raw data
/// </param>
/// <param name="continuations">the continuations
/// </param>
/// <param name="ws">the workbook settings
/// </param>
public SSTRecord(Record t, Record[] continuations, WorkbookSettings ws):base(t)
{
// If a continue record appears in the middle of
// a string, then the encoding character is repeated
// Concatenate everything into one big bugger of a byte array
int totalRecordLength = 0;
for (int i = 0; i < continuations.Length; i++)
{
totalRecordLength += continuations[i].Length;
}
totalRecordLength += getRecord().Length;
sbyte[] data = new sbyte[totalRecordLength];
// First the original data gets put in
int pos = 0;
Array.Copy(getRecord().Data, 0, data, 0, getRecord().Length);
pos += getRecord().Length;
// Now copy in everything else.
continuationBreaks = new int[continuations.Length];
Record r = null;
for (int i = 0; i < continuations.Length; i++)
{
r = continuations[i];
Array.Copy(r.Data, 0, data, pos, r.Length);
continuationBreaks[i] = pos;
pos += r.Length;
}
totalStrings = IntegerHelper.getInt(data[0], data[1], data[2], data[3]);
uniqueStrings = IntegerHelper.getInt(data[4], data[5], data[6], data[7]);
strings = new string[uniqueStrings];
readStrings(data, 8, ws);
}
/// <summary> Reads in all the strings from the raw data
///
/// </summary>
/// <param name="data">the raw data
/// </param>
/// <param name="offset">the offset
/// </param>
/// <param name="ws">the workbook settings
/// </param>
public void readStrings(sbyte[] data, int offset, WorkbookSettings ws)
{
int pos = offset;
int numChars;
sbyte optionFlags;
string s = null;
bool asciiEncoding = false;
bool richString = false;
bool extendedString = false;
int formattingRuns = 0;
int extendedRunLength = 0;
for (int i = 0; i < uniqueStrings; i++)
{
// Read in the number of characters
numChars = IntegerHelper.getInt(data[pos], data[pos + 1]);
pos += 2;
optionFlags = data[pos];
pos++;
// See if it is an extended string
extendedString = ((optionFlags & 0x04) != 0);
// See if string contains formatting information
richString = ((optionFlags & 0x08) != 0);
if (richString)
{
// Read in the crun
formattingRuns = IntegerHelper.getInt(data[pos], data[pos + 1]);
pos += 2;
}
if (extendedString)
{
// Read in cchExtRst
extendedRunLength = IntegerHelper.getInt(data[pos], data[pos + 1], data[pos + 2], data[pos + 3]);
pos += 4;
}
// See if string is ASCII (compressed) or unicode
asciiEncoding = ((optionFlags & 0x01) == 0);
ByteArrayHolder bah = new ByteArrayHolder();
BooleanHolder bh = new BooleanHolder();
bh.Value = asciiEncoding;
pos += getChars(data, bah, pos, bh, numChars);
asciiEncoding = bh.Value;
if (asciiEncoding)
{
s = StringHelper.getString(bah.bytes, numChars, 0, ws);
}
else
{
s = StringHelper.getUnicodeString(bah.bytes, numChars, 0);
}
strings[i] = s;
// For rich strings, skip over the formatting runs
if (richString)
{
pos += 4 * formattingRuns;
}
// For extended strings, skip over the extended string data
if (extendedString)
{
pos += extendedRunLength;
}
if (pos > data.Length)
{
Assert.verify(false, "pos exceeds record .Length");
}
}
}
/// <summary> Gets the chars in the ascii array, taking into account continuation
/// breaks
///
/// </summary>
/// <param name="source">the original source
/// </param>
/// <param name="bah">holder for the new byte array
/// </param>
/// <param name="pos">the current position in the source
/// </param>
/// <param name="ascii">holder for a return ascii flag
/// </param>
/// <param name="numChars">the number of chars in the string
/// </param>
/// <returns> the number of bytes read from the source
/// </returns>
private int getChars(sbyte[] source, ByteArrayHolder bah, int pos, BooleanHolder ascii, int numChars)
{
int i = 0;
bool spansBreak = false;
// byte[] bytes = null;
if (ascii.Value)
{
bah.bytes = new sbyte[numChars];
}
else
{
bah.bytes = new sbyte[numChars * 2];
}
while (i < continuationBreaks.Length && !spansBreak)
{
spansBreak = pos <= continuationBreaks[i] && (pos + bah.bytes.Length > continuationBreaks[i]);
if (!spansBreak)
{
i++;
}
}
// If it doesn't span a break simply do an array copy into the
// destination array and finish
if (!spansBreak)
{
Array.Copy(source, pos, bah.bytes, 0, bah.bytes.Length);
return bah.bytes.Length;
}
// Copy the portion before the break pos into the array
int breakpos = continuationBreaks[i];
Array.Copy(source, pos, bah.bytes, 0, breakpos - pos);
int bytesRead = breakpos - pos;
int charsRead;
if (ascii.Value)
{
charsRead = bytesRead;
}
else
{
charsRead = bytesRead / 2;
}
bytesRead += getContinuedString(source, bah, bytesRead, i, ascii, numChars - charsRead);
return bytesRead;
}
/// <summary> Gets the rest of the string after a continuation break
///
/// </summary>
/// <param name="source">the original bytes
/// </param>
/// <param name="bah">the holder for the new bytes
/// </param>
/// <param name="destPos">the des pos
/// </param>
/// <param name="contBreakIndex">the index of the continuation break
/// </param>
/// <param name="ascii">the ascii flag holder
/// </param>
/// <param name="charsLeft">the number of chars left in the array
/// </param>
/// <returns> the number of bytes read in the continued string
/// </returns>
private int getContinuedString(sbyte[] source, ByteArrayHolder bah, int destPos, int contBreakIndex, BooleanHolder ascii, int charsLeft)
{
int breakpos = continuationBreaks[contBreakIndex];
int bytesRead = 0;
while (charsLeft > 0)
{
Assert.verify(contBreakIndex < continuationBreaks.Length, "continuation break index");
if (ascii.Value && source[breakpos] == 0)
{
// The string is consistently ascii throughout
int length = contBreakIndex == continuationBreaks.Length - 1?charsLeft:System.Math.Min(charsLeft, continuationBreaks[contBreakIndex + 1] - breakpos - 1);
Array.Copy(source, breakpos + 1, bah.bytes, destPos, length);
destPos += length;
bytesRead += length + 1;
charsLeft -= length;
ascii.Value = true;
}
else if (!ascii.Value && source[breakpos] != 0)
{
// The string is Unicode throughout
int length = contBreakIndex == continuationBreaks.Length - 1?charsLeft * 2:System.Math.Min(charsLeft * 2, continuationBreaks[contBreakIndex + 1] - breakpos - 1);
// It looks like the string continues as Unicode too. That's handy
Array.Copy(source, breakpos + 1, bah.bytes, destPos, length);
destPos += length;
bytesRead += length + 1;
charsLeft -= length / 2;
ascii.Value = false;
}
else if (!ascii.Value && source[breakpos] == 0)
{
// Bummer - the string starts off as Unicode, but after the
// continuation it is in straightforward ASCII encoding
int chars = contBreakIndex == continuationBreaks.Length - 1?charsLeft:System.Math.Min(charsLeft, continuationBreaks[contBreakIndex + 1] - breakpos - 1);
for (int j = 0; j < chars; j++)
{
bah.bytes[destPos] = source[breakpos + j + 1];
destPos += 2;
}
bytesRead += chars + 1;
charsLeft -= chars;
ascii.Value = false;
}
else
{
// Double Bummer - the string starts off as ASCII, but after the
// continuation it is in Unicode. This impacts the allocated array
// Reallocate what we have of the byte array so that it is all
// Unicode
sbyte[] oldBytes = bah.bytes;
bah.bytes = new sbyte[destPos * 2 + charsLeft * 2];
for (int j = 0; j < destPos; j++)
{
bah.bytes[j * 2] = oldBytes[j];
}
destPos = destPos * 2;
int length = contBreakIndex == continuationBreaks.Length - 1?charsLeft * 2:System.Math.Min(charsLeft * 2, continuationBreaks[contBreakIndex + 1] - breakpos - 1);
Array.Copy(source, breakpos + 1, bah.bytes, destPos, length);
destPos += length;
bytesRead += length + 1;
charsLeft -= length / 2;
ascii.Value = false;
}
contBreakIndex++;
if (contBreakIndex < continuationBreaks.Length)
{
breakpos = continuationBreaks[contBreakIndex];
}
}
return bytesRead;
}
/// <summary> Gets the string at the specified position
///
/// </summary>
/// <param name="index">the index of the string to return
/// </param>
/// <returns> the strings
/// </returns>
public virtual string getString(int index)
{
Assert.verify(index < uniqueStrings);
return strings[index];
}
}
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?