目的
えとー、base64の文字列に改行を入れたかったんです。
VSで開発するC#の話です。
まぁInsertLineBreaks
を指定すれば、76文字毎に改行コードを入れてくれる事は知ってますけど。
でも、どーしても80文字で改行したいんだよー
まずは力技
string text = "aaaabbbbccccddddeeee";
const int SIZE = 7;
text += new string(' ', (SIZE - text.Length % SIZE) % SIZE);
string[] lines = new string[text.Length / SIZE];
for(int i = 0; i < lines.Length; i++)
lines[i] = text.Substring(i * SIZE, SIZE).TrimEnd();
foreach(string s in lines)
Console.WriteLine("'{0}'", s);
結果はこんな感じで、無問題。
力技ではあるが、予め入力文字列の長さを調整してあるので、切り分けは若干シンプルになっております。
んー
でもなぁ、イマイチ美しくないんだよねー
正規表現って使えないのかなぁ?
お次はやっぱり正規表現。
真っ先に思いつくのはMatches
ですね。
でもMatchCollection
を受けて、foreach
でくるくる回るんじゃ力技と大して変わらない。ここはやっぱりLinq
でしょ。
えー、グーグル先生は物知りなんでー
…
ほらやっぱりあった
string text = "aaaabbbbccccddddeeee";
foreach(string s in Regex.Matches(text, @".{1,7}")
.Cast<Match>()
.Select(m => m.Value)
.ToArray())
Console.WriteLine("'{0}'", s);
こんな感じだー
中々綺麗に纏まりました。
※結果は前と同じなので省略で~す
でも何かコスト高そうだなぁ…
もう一歩踏み込む
そんなこんなで、正規表現確認してたらSplit
ってメソッドがあるじゃん。これならstring[]
で返してくれるのでMatches
よりも良さげ。
んーんん…
でもさ、そんな都合の良い区切り文字なんて無いよー
と云い乍も思い出した。
えーと確かここら辺に(ごそごそっ、と)…
あー、あったあった、これだよ。
何も具体的な文字である必要はない。アンカーで充分って事だよね。
取り敢えずは「肯定的後読み」かな?
(?<=.{80})
とかでどうだろ?
任意の文字80桁の後ろにマッチする訳で…
あれ?ダメだ。
うーーーんと、そっか
一文字ずつ進んでいくから一旦マッチすると、それ以降は一文字ずつマッチしてしまうんだ。
もう一回msdn
をじっくり見てみましょうか。
あ、見っけ。
\G
アンカーだ。前回一致した位置にマッチするからこれを組み合わせれば、必ず指定文字幅毎に一致する筈だ。
(?<=\G.{80})
ばっちり!
と思ったら、問題が一つ。
文字列長が丁度分割文字幅の倍数である時には最後に空文字が出てしまう…
まぁ、そうだよなぁ…
滅多にある事では無し、これで良しとするかなぁ…
いや、ダメだ!
諦めるんじゃないっ
あらびき日記を読み返せ
と云う事で、読み返しました。
否定先読み利用して末尾のみをマッチ対象から外してやればいいんじゃね?
と云う事で以下。
string text = "aaaabbbbccccddddeeee";
foreach(string s in Regex.Split(text, @"(?<=\G.{4})(?!$)"))
Console.WriteLine("'{0}'", s);
素晴らしい
おっといけねー
本来の目的を忘れておるぞ。
分割した文字列を配列で欲しければ此処までの話で良いんだが、改行コードで区切りたい時はどうするか。入手した配列をJoin
でくっつけても良いけど、配列にしてから改めてくっつけると云うのが如何にも遠回り。
此処までくればReplace
メソッド使って、アンカーを改行コードに置き換えれば良い、と云う事に自然と気が付く。
string text = "aaaabbbbccccddddeeee";
Console.WriteLine(Regex.Replace(text, @"(?<=\G.{4})(?!$)", Environment.NewLine));
すんげーぞ
めちゃ簡潔になった…
しかし、結果的にお馴染みReplace
メソッドに落ち着いたけど、Split
に気が付いたからこそ、此処に辿り着いた気がする。最初っからすんなり気が付ける様に精進しなくては、と思いました。