数値以外の範囲を拡張できるオープンソースのライブラリまたはアルゴリズムがあるかどうか疑問に思っていました。たとえば、必要な場合は取得する必要があり1A
ます9A
1A, 2A, 3A, 4A, 5A, 6A, 7A, 8A, 9A.
これについてグーグルを試してみましたが、思いつくことができる最高のものは、数値をダッシュで展開する正規表現でした(1-3が1,2,3になります)。
他の人が指摘したように、より具体的にすると便利です。思いついた文字列の任意の順序に従って範囲を生成するライブラリがあるとは期待できないと思います。
特定の文字列の後継者が何であるかを簡単に定義できる場合、解決策は非常に簡単です。つまり、文字列に後継関数がある場合S
(たとえば を使用S('3A') = '4A'
)、次のようなものを使用できます。
s = initial_string
while s != final_string do
output s
s = S(s)
output s
過去に、指定された長さと指定されたl
文字範囲b
のすべての文字列を生成するために使用したものe
は、次の (疑似) コードです。幅広いバリエーションに簡単に適応できます。
// initialise s with b at every position
for i in [0..l) do
s[i] = b
done = false
while not done do
output s
j = 0
// if s[j] is e, reset it to b and "add carry"
while j < l and s[j] == e do
s[j] = b
j = j + 1
if j == l then
done = true
if not done then
s[j] = s[j] + 1
たとえば、特定の文字列で開始するには、初期化を変更するだけで済みます。終了を設定するには、内側の while の動作を変更して位置を個別に処理するだけですl
(その位置の終了文字列の文字に制限し、到達した場合は減少しますl
)。
開始範囲と終了範囲が同じ交互パターンに従い、桁の範囲を と に制限すると仮定すると、桁0-9
のA-Z
各グループを多次元座標のコンポーネントと考えることができます。たとえば1A
、2 次元座標(1,A)
(Excel が行と列の 2 次元グリッドにラベルを付けるために使用するもの) に対応します。一方AA1BB2
、4 次元座標になり(AA,1,BB,2)
ます。
各コンポーネントは独立しているため、2 つの座標間の範囲を拡張するには、各コンポーネントの拡張のすべての組み合わせを返すだけです。以下は、今日の午後に作成した簡単な実装です。通常の数字とアルファベット数字の任意の数の交互に機能し、大きなアルファベット範囲 (つまり、AB
~CDE
だけでなくAB
~CD
) を処理します。
注:これは、実際の実装のラフ ドラフトとして意図されています (私は明日出発するので、いつもより洗練されていません ;)。エラー処理、堅牢性、(読みやすさ;)などに関する通常の警告がすべて適用されます。
IEnumerable<string> ExpandRange( string start, string end ) {
// Split coordinates into component parts.
string[] startParts = GetRangeParts( start );
string[] endParts = GetRangeParts( end );
// Expand range between parts
// (i.e. 1->3 becomes 1,2,3; A->C becomes A,B,C).
int length = startParts.Length;
int[] lengths = new int[length];
string[][] expandedParts = new string[length][];
for( int i = 0; i < length; ++i ) {
expandedParts[i] = ExpandRangeParts( startParts[i], endParts[i] );
lengths[i] = expandedParts[i].Length;
}
// Return all combinations of expanded parts.
int[] indexes = new int[length];
do {
var sb = new StringBuilder( );
for( int i = 0; i < length; ++i ) {
int partIndex = indexes[i];
sb.Append( expandedParts[i][partIndex] );
}
yield return sb.ToString( );
} while( IncrementIndexes( indexes, lengths ) );
}
readonly Regex RangeRegex = new Regex( "([0-9]*)([A-Z]*)" );
string[] GetRangeParts( string range ) {
// Match all alternating digit-letter components of coordinate.
var matches = RangeRegex.Matches( range );
var parts =
from match in matches.Cast<Match>( )
from matchGroup in match.Groups.Cast<Group>( ).Skip( 1 )
let value = matchGroup.Value
where value.Length > 0
select value;
return parts.ToArray( );
}
string[] ExpandRangeParts( string startPart, string endPart ) {
int start, end;
Func<int, string> toString;
bool isNumeric = char.IsDigit( startPart, 0 );
if( isNumeric ) {
// Parse regular integers directly.
start = int.Parse( startPart );
end = int.Parse( endPart );
toString = ( i ) => i.ToString( );
}
else {
// Convert alphabetic numbers to integers for expansion,
// then convert back for display.
start = AlphaNumberToInt( startPart );
end = AlphaNumberToInt( endPart );
toString = IntToAlphaNumber;
}
int count = end - start + 1;
return Enumerable.Range( start, count )
.Select( toString )
.Where( s => s.Length > 0 )
.ToArray( );
}
bool IncrementIndexes( int[] indexes, int[] lengths ) {
// Increment indexes from right to left (i.e. Arabic numeral order).
bool carry = true;
for( int i = lengths.Length; carry && i > 0; --i ) {
int index = i - 1;
int incrementedValue = (indexes[index] + 1) % lengths[index];
indexes[index] = incrementedValue;
carry = (incrementedValue == 0);
}
return !carry;
}
// Alphabetic numbers are 1-based (i.e. A = 1, AA = 11, etc, mod base-26).
const char AlphaDigitZero = (char)('A' - 1);
const int AlphaNumberBase = 'Z' - AlphaDigitZero + 1;
int AlphaNumberToInt( string number ) {
int sum = 0;
int place = 1;
foreach( char c in number.Cast<char>( ).Reverse( ) ) {
int digit = c - AlphaDigitZero;
sum += digit * place;
place *= AlphaNumberBase;
}
return sum;
}
string IntToAlphaNumber( int number ) {
List<char> digits = new List<char>( );
while( number > 0 ) {
int digit = number % AlphaNumberBase;
if( digit == 0 ) // Compensate for 1-based alphabetic numbers.
return "";
char c = (char)(AlphaDigitZero + digit);
digits.Add( c );
number /= AlphaNumberBase;
}
digits.Reverse( );
return new string( digits.ToArray( ) );
}
ソリューションの最初のステップは、文字と数字がどのように相互作用し、シーケンスを形成するかを定義することです。少なくとも 1A, 1B .... 8Y, 8Z, 9A を実行すると仮定するため、指定された例は明確ではありません。これは、入力が10進数に制限され、その後に1文字が続くことを前提としています。
文字と小数の連続したシーケンスを定義できる場合は、そのシーケンスの一部を生成するための再帰/ループの問題になります。
たとえば、入力の各文字が (1-9A-Z) の 1 つであると想定できるため、アルファ文字の 10 進数の ASCII 値を取得して 55 を減算することで簡単に連続させることができ、実際には範囲が得られます。 (1-35)
可能性の数が驚異的であるため、私はそれをいくらか開いたままにしようとしていました. ここで多くの技術的な詳細を調べないと 100% 答えられなかった質問の 1 つは、「良い」または「悪い」範囲と見なされると思います。私は、他の人々がこの問題にどのように取り組んだかについて、アイデアの出発点を見つけようとしています。誰かがこの問題を解決する方法を説明するブログ投稿を書いたり、これを処理するためのライブラリ全体を作成したりすることを望んでいました.