Quelle est la fonction c # la plus rapide qui prend et int et renvoie une chaîne contenant une lettre ou des lettres à utiliser dans une fonction Excel? Par exemple, 1 renvoie "A", 26 renvoie "Z", 27 renvoie "AA", etc.
Cette opération s'appelle des dizaines de milliers de fois et prend 25% du temps nécessaire pour générer un grand tableur contenant de nombreuses formules.
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();
}
Je l'utilise actuellement avec 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;
}
et
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;
}
Comme mentionné dans d'autres articles, les résultats peuvent être mis en cache.
Je peux vous dire que la fonction la plus rapide ne sera pas la plus jolie. C'est ici:
private string[] map = new string[]
{
"A", "B", "C", "D", "E" .............
};
public string getColumn(int number)
{
return map[number];
}
Ne le convertis pas du tout. Excel peut fonctionner aussi bien en notation R1C1 qu'en notation A1.
Donc (excuses pour utiliser VBA plutôt que C #):
Application.Worksheets("Sheet1").Range("B1").Font.Bold = True
peut tout aussi bien s’écrire:
Application.Worksheets("Sheet1").Cells(1, 2).Font.Bold = True
La propriété Range
prend la notation A1 alors que la propriété Cells
prend (numéro de ligne, numéro de colonne).
Pour sélectionner plusieurs cellules: Range(Cells(1, 1), Cells(4, 6))
(NB aurait besoin d'un qualificatif d'objet s'il n'utilisait pas la feuille de calcul active) plutôt que Range("A1:F4")
La propriété Columns
peut prendre une lettre (par exemple F) ou un nombre (par exemple 6)
Voici ma version: Il n'y a pas de limitation en tant que telle de 2 lettres ou de 3 lettres. Entrez simplement le nombre requis (en commençant à 0) Renverra l'en-tête de colonne Excel comme une séquence alphabétique pour la transmission. nombre:
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;
}
Je n'ai pas testé cela pour la performance, si quelqu'un peut le faire, ce sera formidable pour les autres. (Désolé d'être paresseux) :)
À votre santé!
Vous pouvez pré-générer toutes les valeurs dans un tableau de chaînes. Cela prendrait très peu de mémoire et pourrait être calculé dès le premier appel.
Ceci est écrit en Java, mais c'est fondamentalement la même chose.
Voici le code pour calculer le libellé de la colonne, en majuscule, avec un index basé sur 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);
}
Voici le code pour calculer un index de base 0 pour la colonne à partir de l'étiquette en majuscule:
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;
}
Le plus RAPIDE absolu serait de capitaliser sur le fait que la feuille de calcul Excel ne contient qu'un nombre fixe de colonnes, de sorte que vous feriez une table de correspondance. Déclarez un tableau de chaînes constantes de 256 entrées et pré-remplissez-le avec les chaînes de "A" à "IV". Ensuite, vous effectuez simplement une recherche directe dans l'index.
Essayez cette fonction.
// 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);
}
Maintenant, il ne faut pas utiliser that beaucoup de mémoire pour pré-générer chaque nom de colonne pour chaque index et les stocker dans un seul grand tableau, vous n'avez donc pas besoin de rechercher le nom d'une colonne deux fois .
Si je peux penser à d'autres optimisations, je les ajouterai plus tard, mais je pense que cette fonction devrait être assez rapide, et je doute que vous ayez besoin de cette vitesse si vous faites la pré-génération.
Une fois votre fonction exécutée, laissez-la en cache les résultats dans un dictionnaire. Donc, cela ne nécessitera plus de calcul.
par exemple. Convert (27) vérifiera si 27 est mappé/stocké dans le dictionnaire. Sinon, effectuez le calcul et enregistrez "AA" contre 27 dans le dictionnaire.
Votre premier problème est que vous déclarez 6 variables dans la méthode. Si un methd doit être appelé des milliers de fois, le simple fait de déplacer ceux-ci dans la portée de classe au lieu de portée de fonction réduira probablement votre temps de traitement de plus de la moitié dès le départ.
Voici une implémentation concise utilisant 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;
}
Ceci génère A
à Z
, puis AA
à ZZ
, puis AAA
à ZZZ
.
Sur mon PC, appeler GetExcelStrings().ToArray()
prend environ 30 ms. Par la suite, vous pouvez vous référer à ce tableau de chaînes si vous en avez besoin des milliers de fois.
@Neil N - Beau code Je pense que la troisième lettre devrait avoir un +64 plutôt que +65? ai-je raison?
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();
}
Ma solution:
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();
}
}
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) ;
}
désolé il y avait un changement. corrigée.
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];
}
}
La mise en cache réduit réellement le temps d'exécution de 10 000 000 d'appels aléatoires à un tiers de sa valeur, même si:
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;
}
Je pense que la mise en cache dans le pire des cas (frapper toutes les valeurs) ne pourrait pas prendre plus de 250 Ko (17576 valeurs possibles * (sizeof (int) = 4 + sizeof (car)) * 3 + surcharge de chaîne = 2)
l'idée de barrowc est beaucoup plus pratique et rapide que toute fonction de conversion! J'ai converti ses idées en code c # réel que j'utilise:
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;
Utilisez simplement une formule Excel à la place d'une fonction définie par l'utilisateur (UDF) ou d'un autre programme, comme le dit Allen Wyatt ( https://Excel.tips.net/T003254_Alphabetic_Column_Designation.html ):
=SUBSTITUTE(ADDRESS(ROW(),COLUMN(),4),ROW(),"")
(Dans mon organisation, utiliser des FDU serait très pénible.)
Pourquoi n'essayons-nous pas factorielle?
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] + "";
}
Le code que je fournis n'est PAS C # (à la place, c'est python), mais la logique peut être utilisée pour n'importe quel langage.
La plupart des réponses précédentes sont correctes. Voici un autre moyen de convertir le numéro de colonne en colonnes Excel. La solution Est plutôt simple si nous considérons cela comme une conversion de base. Convertissez simplement le numéro de colonne en base 26 car il n’ya que 26 lettres. Voici comment procéder:
étapes:
définir la colonne comme un quotient
soustrayez-en une à la variable quotient (de l'étape précédente) car nous devons nous retrouver sur table ascii avec 97 étant a.
diviser par 26 et obtenir le reste.
voici le code qui fait ça :)
def convert_num_to_column(column_num):
result = ""
quotient = column_num
remainder = 0
while (quotient >0):
quotient = quotient -1
remainder = quotient%26
result = chr(int(remainder)+97)+result
quotient = int(quotient/26)
return result
print("--",convert_num_to_column(1).upper())
C'est récursif. Rapide et à droite:
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];
}
}