【C#】【outlook】【URL検出】outlookのセーフリンクを力技(正規表現)で元のリンクに修正する

# 正規表現とかStringの関数をゴリゴリに使ってやってみる

意外と手こずったのでメモ程度に記載。
詳細はできるだけ解説するつもり。

## 正規表現とは

String.Contain()関数で文字列を検索すると**一つの文字列に対しての結果が出てくる**けど、URLとかの**可変性**を持っているものに対してのString.Contain()とかString.Replace()をやりたいとかの時に使えるやつ。
つまり、文字列が可変の時にも使える検索システムの拡張版的なやつ(本人の認識、間違ってたらごめん)。

棒読みちゃんとかで使ってたりするあれ。

## なんで必要なの？

今回使ったのはタイトル通り ~~(忌々しい)~~ セーフリンクを突破してHTMLを解析したかったから。
HTMLまで解析できなくてもセーフリンクを突破できればいいかなみたいな感じで進めた。

## 方針

今回の方針として、下の感じで進めた。

1. ドメインとかの頭の方を削除する
1. 最後の方のなんかごちゃごちゃついてる部分を削除する
1. エンコード方式がおかしかったのでUTF-8に修正する

## 最初に

セーフリンクの形はこんな感じになってる
![url.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689137/028e8950-2703-fd6a-a100-11e8b429374b.png)
んで結局画像の赤で塗りつぶしてるところを出したいからいろいろやる。

## 先に全文ソース

ほとんどの人がソース目当てだろうから先にどうぞ。
(エンコードとかはもう先にやっちゃってるから後から書くわ)

```cs
//using System.Text.RegularExpressions;

string link = "ここに元のセーフリンクを入れる";
string safeLinkDomain = Regex.Match(link, @"http(s)?://nam[0-9]{2}\.safelink(s)?\.protection\.outlook\.com/\?url\=").Value;
string accessUrl;
if (safeLinkDomain.Length > 0)
{
    accessUrl = link.Replace(safeLinkDomain, string.Empty);
    int indexOf = accessUrl.IndexOf("&amp;data=");
    string replaceOf = accessUrl.Substring(lastIndexOf, accessUrl.Length - lastIndexOf);
    accessUrl = accessUrl.Replace(replaceOf, string.Empty);
}
else accessUrl = link;

//この下にURLのアクセスを書くなり...
//new HttpClient()とかnew WebClient()とかのURL指定部分にaccessUrlを指定すれば動くはず
```

## 解説

やってることとしては先の [方針](#方針) と同じ。頭を消して最後を消して終わり。
んじゃ順番に解説していきます(わからんかったら他の方々の正規表現の解説を見てくれ)。

⑴ ドメインとかの頭の方を消す
消すというか検出するのが先っぽいね。

```cs
string safeLinkDomain = Regex.Match(link, @"http(s)?://nam[0-9]{2}\.safelink(s)?\.protection\.outlook\.com/\?url\").Value;
if (safeLinkDomain.Length > 0)
```

System.Text.RegularExpressions.Regex.Match()関数を使って頭の方を全体的に検出してる(目的のURLよりも前まで)。
んで~Match().Valueで検出した結果を取得できるから取得する。
if構文を使って、検出できたかどうかを判別する。

```cs
accessUrl = link.Replace(safeLinkDomain, string.Empty);
```

これで最初の部分を消す。

⑵ 最後の方のなんかごちゃごちゃついてる部分を削除する
こっちの方が正直時間かかった。

```cs
int indexOf = accessUrl.IndexOf("&amp;data=");
string replaceOf = accessUrl.Substring(lastIndexOf, accessUrl.Length - lastIndexOf);
accessUrl = accessUrl.Replace(replaceOf, string.Empty);
```

まず、``String.IndexOf()``関数で``"&amp;data="``(元URLの直後に来るやつ)の場所を検出する。
次に、``String.SubString()``関数で``"&amp;data="``の後の文字列を全部取り出す。
最後に、``String.Replace()``関数で入力されたURLから削除する。

これで最後の方の長ったらしいアルファベットが消せる。

基本的にはこの2工程で終わる。

3についてはちょっと後で。

## 正規表現

使ってる正規表現がどんな感じになってるかわからなかったら(私もわからん。間違ってたらごめん)。

```
http(s)?://nam[0-9]{2}\.safelink(s)?\.protection\.outlook\.com/\?url\
```

まず、``(s)?``についてる``?``は``"s"の文字が入ってる場合もあるし入ってない場合もあるよ``っていうことを示してる感じだと思う。
次に、``[0-9]{2}``は``"0~9"の"2"桁の数字があるよ``っていうこと示してる。
だから``[3-9]{4}``ってなったら``"3~9"の"4"桁の数字が入るよ``っていうことになる。
そして、``\.``とか``\?``とかは、``.``とか``?``とかが正規表現の処理に使われてて、単体で書いたら処理されちゃうから``\``の文字を入れて、「これは文字列として扱いますよ～」っていうことを示してる。

まぁ、正直``http(s)?``なんて書かなくていいんだけど...まぁ...いつかhttpになるかもしれないし...ね...？

ちなみに正規表現は上のごちゃごちゃしたフィルタリングの文字列を入れるだけで有効かをしらべてくれるサイトがあったりする。
https://regex101.com/

## エンコード

使わないかもしれないけど、URLの``://``が``%3A%2F%2F``とかいうURLエンコードになってた時にいつも使ってる形式に修正する方法を書いておく。

```cs
//using System.Web;
//using System.Text;
//System.Web.dllの参照設定が必要

Encoding encoding = Encoding.UTF8; //ここに元文字列のエンコード形式
accessUrl = HttpUtility.UrlDecode(accessUrl, encoding);
//accessUrlは上のコードのやつの文字列のこと
```

かんたんに書くとこれ。
``System.Web.HttpUtility.UrlDecode()``関数がいい感じに出コードしてくれる。
``HttpClient()``関数とかでURLを設定したときにアクセスできませんよって言われたときに試してみてほしい。私はこれでうまくいった。

## (おまけ)セーフリンク全体を検出する

``Regex.Match()``関数を使ってこれも簡単に検出できる。

```cs
string safeLink = Regex.Match(link, @"http(s)?://nam[0-9]{2}\.safelink(s)?\.protection\.outlook\.com/\?url\=([\w\- ./?%&=~;]*)?").Value;
```

正規表現の最初の方については割愛。
```
([\w\- ./?%&=~;]*)?
```
ここは``\w``が『アルファベット、アンダーバー、数字』を検出、``\-``が``-``を検出、後の``]``の前までの記号たちは普通にそれぞれを検出する。
``*``は検出回数が決まってないことを示す(``*``がなかったら最初の一文字しかとってくれない)。
``?``は検出がないかもしれないことを示す(もしかしたらいらないかも(わからん))。

みたいな感じでできる(一応上のサイトで試してみて普通に作動することも検証済み&実際のアプリでも動いた)。

## 感想

使う人いるか分からんが情報提供程度に。
正直、正規表現なんて全然使わないからわからんかったから疲れた。
やった後は正規表現神って思った。
Containsで一つずつ調べなくていいんだもの。

楽しかった。終わり。