texto secuencia letras incrementar formato escribir desde dar consecutivas archivo c# excel performance

secuencia - La función más rápida para generar letras de columna de Excel en C#



letras consecutivas en excel (20)

¿Por qué no probamos factorial?

public static string GetColumnName(int index) { const string letters = "ZABCDEFGHIJKLMNOPQRSTUVWXY"; int NextPos = (index / 26); int LastPos = (index % 26); if (LastPos == 0) NextPos--; if (index > 26) return GetColumnName(NextPos) + letters[LastPos]; else return letters[LastPos] + ""; }

¿Cuál es la función c # más rápida que toma e int y devuelve una cadena que contiene una letra o letras para su uso en una función de Excel? Por ejemplo, 1 devuelve "A", 26 devuelve "Z", 27 devuelve "AA", etc.

Esto se llama decenas de miles de veces y toma el 25% del tiempo necesario para generar una gran hoja de cálculo con muchas fórmulas.

public string Letter(int intCol) { int intFirstLetter = ((intCol) / 676) + 64; int intSecondLetter = ((intCol % 676) / 26) + 64; int intThirdLetter = (intCol % 26) + 65; char FirstLetter = (intFirstLetter > 64) ? (char)intFirstLetter : '' ''; char SecondLetter = (intSecondLetter > 64) ? (char)intSecondLetter : '' ''; char ThirdLetter = (char)intThirdLetter; return string.Concat(FirstLetter, SecondLetter, ThirdLetter).Trim(); }


@Neil N - buen código Creo que la tercera carta debe tener un +64 en lugar de +65? estoy en lo cierto?

public string Letter(int intCol) { int intFirstLetter = ((intCol) / 676) + 64; int intSecondLetter = ((intCol % 676) / 26) + 64; int intThirdLetter = (intCol % 26) + 65; '' SHOULD BE + 64? char FirstLetter = (intFirstLetter > 64) ? (char)intFirstLetter : '' ''; char SecondLetter = (intSecondLetter > 64) ? (char)intSecondLetter : '' ''; char ThirdLetter = (char)intThirdLetter; return string.Concat(FirstLetter, SecondLetter, ThirdLetter).Trim(); }


Actualmente uso esto, con Excel 2007

public static string ExcelColumnFromNumber(int column) { string columnString = ""; decimal columnNumber = column; while (columnNumber > 0) { decimal currentLetterNumber = (columnNumber - 1) % 26; char currentLetter = (char)(currentLetterNumber + 65); columnString = currentLetter + columnString; columnNumber = (columnNumber - (currentLetterNumber + 1)) / 26; } return columnString; }

y

public static int NumberFromExcelColumn(string column) { int retVal = 0; string col = column.ToUpper(); for (int iChar = col.Length - 1; iChar >= 0; iChar--) { char colPiece = col[iChar]; int colNum = colPiece - 64; retVal = retVal + colNum * (int)Math.Pow(26, col.Length - (iChar + 1)); } return retVal; }

Como se menciona en otras publicaciones, los resultados se pueden almacenar en caché.


Aquí está mi versión: Esto no tiene ninguna limitación como tal, de 2 letras o 3 letras. Simplemente ingrese el número requerido (comenzando con 0) Devolverá el encabezado de la columna de Excel como secuencia de alfabeto para el número transferido:

private string GenerateSequence(int num) { string str = ""; char achar; int mod; while (true) { mod = (num % 26) + 65; num = (int)(num / 26); achar = (char)mod; str = achar + str; if (num > 0) num--; else if (num == 0) break; } return str; }

No probé esto para el rendimiento, si alguien puede hacer eso será genial para los demás. (Perdón por ser flojo) :)

¡Aclamaciones!


Aquí hay una implementación concisa usando LINQ.

static IEnumerable<string> GetExcelStrings() { string[] alphabet = { string.Empty, "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" }; return from c1 in alphabet from c2 in alphabet from c3 in alphabet.Skip(1) // c3 is never empty where c1 == string.Empty || c2 != string.Empty // only allow c2 to be empty if c1 is also empty select c1 + c2 + c3; }

Esto genera A a Z , luego AA a ZZ , luego AAA a ZZZ .

En mi PC, llamar a GetExcelStrings().ToArray() tarda unos 30 ms. A partir de entonces, puede consultar este conjunto de cadenas si lo necesita miles de veces.


El MÁS RÁPIDO, sería capitalizar que la hoja de cálculo de Excel solo tenga un número fijo de columnas, por lo que haría una tabla de búsqueda. Declare un conjunto de cadenas constante de 256 entradas y prepárela con las cadenas de "A" a "IV". Entonces simplemente haces una búsqueda directa del índice.


Es recursivo Rápido y correcto:

class ToolSheet { //Not the prettyest but surely the fastest : static string[] ColName = new string[676]; public ToolSheet() { ColName[0] = "A"; for (int index = 1; index < 676; ++index) Recurse(index, index); } private int Recurse(int i, int index) { if (i < 1) return 0; ColName[index] = ((char)(65 + i % 26)).ToString() + ColName[index]; return Recurse(i / 26, index); } public string GetColName(int i) { return ColName[i - 1]; } }


Esto está escrito en Java, pero básicamente es lo mismo.

Aquí hay un código para calcular la etiqueta de la columna, en mayúsculas, con un índice basado en 0:

public static String findColChars(long index) { char[] ret = new char[64]; for (int i = 0; i < ret.length; ++i) { int digit = ret.length - i - 1; long test = index - powerDown(i + 1); if (test < 0) break; ret[digit] = toChar(test / (long)(Math.pow(26, i))); } return new String(ret); } private static char toChar(long num) { return (char)((num % 26) + 65); }

Aquí hay un código para calcular el índice basado en 0 para la columna de la etiqueta en mayúscula:

public static long findColIndex(String col) { long index = 0; char[] chars = col.toCharArray(); for (int i = 0; i < chars.length; ++i) { int cur = chars.length - i - 1; index += (chars[cur] - 65) * Math.pow(26, i); } return index + powerDown(chars.length); } private static long powerDown(int limit) { long acc = 0; while (limit > 1) acc += Math.pow(26, limit-- - 1); return acc; }


Mi solución:

static class ExcelHeaderHelper { public static string[] GetHeaderLetters(uint max) { var result = new List<string>(); int i = 0; var columnPrefix = new Queue<string>(); string prefix = null; int prevRoundNo = 0; uint maxPrefix = max / 26; while (i < max) { int roundNo = i / 26; if (prevRoundNo < roundNo) { prefix = columnPrefix.Dequeue(); prevRoundNo = roundNo; } string item = prefix + ((char)(65 + (i % 26))).ToString(CultureInfo.InvariantCulture); if (i <= maxPrefix) { columnPrefix.Enqueue(item); } result.Add(item); i++; } return result.ToArray(); } }


No lo convierta en absoluto. Excel puede trabajar en notación R1C1 tan bien como en notación A1.

Entonces (disculpas por usar VBA en lugar de C #):

Application.Worksheets("Sheet1").Range("B1").Font.Bold = True

puede escribirse tan fácilmente como:

Application.Worksheets("Sheet1").Cells(1, 2).Font.Bold = True

La propiedad Range toma la notación A1 mientras que la propiedad Cells toma (número de fila, número de columna).

Para seleccionar varias celdas: Range(Cells(1, 1), Cells(4, 6)) (NB necesitaría algún tipo de calificador de objeto si no utiliza la hoja de cálculo activa) en lugar de Range("A1:F4")

La propiedad Columns puede tomar una letra (por ejemplo, F) o un número (por ejemplo, 6)


Prueba esta función.

// Returns name of column for specified 0-based index. public static string GetColumnName(int index) { var name = new char[3]; // Assumes 3-letter column name max. int rem = index; int div = 17576; // 26 ^ 3 for (int i = 2; i >= 0; i++) { name[i] = alphabet[rem / div]; rem %= div; div /= 26; } if (index >= 676) return new string(name, 3); else if (index >= 26) return new string(name, 2); else return new string(name, 1); }

Ahora no debería ocupar tanta memoria pregenerar cada nombre de columna para cada índice y almacenarlos en una sola matriz enorme, por lo que no debería necesitar buscar el nombre de ninguna columna dos veces.

Si puedo pensar en otras optimizaciones, las agregaré más adelante, pero creo que esta función debería ser bastante rápida, y dudo que necesites este tipo de velocidad si haces la pregeneración.


Puede pregenerar todos los valores en una matriz de cadenas. Esto llevaría muy poca memoria y podría calcularse en la primera llamada.


Puedo decirte que la función más rápida no será la función más bonita. Aquí está:

private string[] map = new string[] { "A", "B", "C", "D", "E" ............. }; public string getColumn(int number) { return map[number]; }


Sin embargo, el almacenamiento en caché realmente reduce el tiempo de ejecución de 10,000,000 llamadas aleatorias a 1/3 de su valor:

static Dictionary<int, string> LetterDict = new Dictionary<int, string>(676); public static string LetterWithCaching(int index) { int intCol = index - 1; if (LetterDict.ContainsKey(intCol)) return LetterDict[intCol]; int intFirstLetter = ((intCol) / 676) + 64; int intSecondLetter = ((intCol % 676) / 26) + 64; int intThirdLetter = (intCol % 26) + 65; char FirstLetter = (intFirstLetter > 64) ? (char)intFirstLetter : '' ''; char SecondLetter = (intSecondLetter > 64) ? (char)intSecondLetter : '' ''; char ThirdLetter = (char)intThirdLetter; String s = string.Concat(FirstLetter, SecondLetter, ThirdLetter).Trim(); LetterDict.Add(intCol, s); return s; }

Creo que el almacenamiento en caché en el peor de los casos (acierta cada valor) no podría tomar más de 250kb (17576 valores posibles * (sizeof (int) = 4 + sizeof (char) * 3 + string overhead = 2)



Su primer problema es que está declarando 6 variables en el método. Si se va a llamar a un método miles de veces, simplemente moverlo al ámbito de la clase en lugar del alcance de la función probablemente reducirá el tiempo de procesamiento en más de la mitad desde el principio.


Una vez que se haya ejecutado su función, déjelo en caché los resultados en un diccionario. Entonces, no tendrá que volver a hacer el cálculo.

por ejemplo, Convert (27) verificará si 27 está mapeado / almacenado en el diccionario. Si no, haga el cálculo y almacene "AA" contra 27 en el diccionario.


la idea de barrowc es mucho más conveniente y más rápida que cualquier función de conversión. He convertido sus ideas al código actual de c # que uso:

var start = m_xlApp.Cells[nRow1_P, nCol1_P]; var end = m_xlApp.Cells[nRow2_P, nCol2_P]; // cast as Range to prevent binding errors m_arrRange = m_xlApp.get_Range(start as Range, end as Range); object[] values = (object[])m_arrRange.Value2;


lo siento, hubo un cambio. corregido

class ToolSheet { //Not the prettyest but surely the fastest : static string[] ColName = new string[676]; public ToolSheet() { for (int index = 0; index < 676; ++index) { Recurse(index, index); } } private int Recurse(int i, int index) { if (i < 1) { if (index % 26 == 0 && index > 0) ColName[index] = ColName[index - 1].Substring(0, ColName[index - 1].Length - 1) + "Z"; return 0; } ColName[index] = ((char)(64 + i % 26)).ToString() + ColName[index]; return Recurse(i / 26, index); } public string GetColName(int i) { return ColName[i - 1]; } }


private String columnLetter(int column) { if (column <= 0) return ""; if (column <= 26){ return (char) (column + 64) + ""; } if (column%26 == 0){ return columnLetter((column/26)-1) + columnLetter(26) ; } return columnLetter(column/26) + columnLetter(column%26) ; }