質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

1回答

1564閲覧

テキスト内のURLまたはマークダウンで書かれたものをaタグに置き換えたい

kawauso

総合スコア56

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2021/08/30 09:29

編集2021/08/30 10:57

前提・実現したいこと

お世話になります。現在、ASP.NETでWebシステムを作成しております。
ユーザーが書いた文章にリンクを設定できるようにしたいと思っております。

仕様は下記のとおりです。

  1. URLのみであれば<a href="https://www.google.co.jp/">https://www.google.co.jp/</a>のように変換する
  2. [Google](https://www.google.co.jp/)のように書かれていれば<a href="https://www.google.co.jp/">Google</a>のように変換する(Markdownに準拠)
  3. URLのすぐ後にアルファベットや数字などが来る場合は半角スペースなどを入力してもらうようにする

たとえば、

[Google](https://www.google.co.jp/)にアクセスしてください。 テラテイルはhttps://teratail.com/こちらです。

といった文章を書くと下記のようにしたいです。

Googleにアクセスしてください。
テラテイルはhttps://teratail.com/こちらです。

該当のソースコード

正規表現を使って文字列を置換するを参考にして1の仕様は実装できましたが、
2の形式で書かれていた場合の方法を検討しましたが、解決には至りませんでした。

C#

1string text= "グーグルはhttps://www.google.co.jp/こちら!"; 2text= System.Text.RegularExpressions.Regex.Replace(text, @"s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+", "<a href=\"$&\">$&</a>"); 3 4Console.WrriteLine(text); 5// 出力 6// グーグルは<a href="https://www.google.co.jp/">https://www.google.co.jp/</a>こちら!

解決方法またはヒントをご存じの方がいらっしゃいましたらよろしくお願いいたします。

補足情報(FW/ツールのバージョンなど)

C# 7, VisualStudio 2019

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/08/30 09:44

ASP.NET はどう関係するのですか? 「該当のソースコード」を見ると、単に文字列の書き換えの問題で ASP.NET は関係ないように見えますが。
kawauso

2021/08/30 09:48

ASP.NETで開発していてるシステムで使用したかったためASP.NETのタグを追加しました。混乱を招く可能性があるためASP.NETのタグは削除しました。
退会済みユーザー

退会済みユーザー

2021/08/30 10:04 編集

条件をきちんと書かないとダメだと思いますけど? 例えば、 "googlehttps://www.google.co.jp/here" とかなっていることはないのですか? ちなみに、上記がここで変換されると here まで URL に含まれれてしまいます。
kawauso

2021/08/30 10:13

そのような場合だとhereまでURLに含まれてしまうのは確認済みです。URLの後にアルファベットや数字が続く場合はスペースを空けたもらうなどを行おうと考えています。その後スペースを""に置換しようなどは考えておりません。仕様の1,2を満たせればと考えております。
退会済みユーザー

退会済みユーザー

2021/08/30 10:29

> URLの後にアルファベットや数字が続く場合はスペースを空けたもらうなどを行おうと考えています。 それを質問する際の条件に明記しようと言っているのですが。
kawauso

2021/08/30 10:58

失礼しました。修正しました。
guest

回答1

0

ベストアンサー

対応方法

実装1、実装2ともに、基本的な考え方は同じです。
次のパターン、それぞれの正規表現を準備します。

  1. https://www.google.co.jp/のみにマッチする正規表現
  2. [Google](https://www.google.co.jp/)のみにマッチする正規表現

考慮無しに正規表現を準備してしまうと、1の正規表現で2のパターンが一致してしまいます。
そのため、前方否定戻り読み「(?<!~)」という機能を使って、「(」や「"」で囲まれている場合は1に該当させないという条件を付け加えます。

また、テキストの一部を組み替える必要があるので、名前付きグループ化を行い、フォーマットを指定して置換を行うようにしています。

実装に関して

単体テスト形式で回答いたします。
なお、XSS等の考慮はしていないので、ご注意ください。

実装例1: 2段階で置換を行うパターン

cs

1[TestMethod] 2public void TestTeratail356904_1() 3{ 4 var expressionUrl = "s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+"; 5 6 // グループ化(置換でこの部分を抜き出すため) 7 var expressionUrlGroup = "(?<url>" + expressionUrl + ")"; 8 9 // 正規表現 URLのみ: 正規表現の前方否定戻り読み「(?<!~)」でカッコやダブルクォーテーションがマッチしないようにする 10 var expressionUrlOnly = "(?<![\(\\"]\s*)" + expressionUrlGroup + "(?!\s*[\)\\"])"; 11 // ^^^^^^^ カッコやダブルクォーテーション 12 // ^^^^ スペースを入れた場合の考慮 13 14 // 正規表現 マークダウン 15 var expressionMarkdown = "\[(?<text>[^\]]*)\]\(\s*" + expressionUrlGroup + "\s*\)"; 16 17 // 正規表現 URLのみ 18 var regexUrlOnly = new Regex(expressionUrlOnly, 19 RegexOptions.IgnoreCase // 大文字小文字を区別しない 20 | RegexOptions.Compiled // コンパイルする(この正規表現を使いまわす場合) 21 ); 22 // 正規表現 マークダウン 23 var regexMarkdown = new Regex(expressionMarkdown, 24 RegexOptions.IgnoreCase // 大文字小文字を区別しない 25 | RegexOptions.Compiled // コンパイルする(この正規表現を使いまわす場合) 26 ); 27 28 // 置換元文字列 29 var sb = new StringBuilder(); 30 sb.AppendLine("[Google](https://www.google.co.jp/)にアクセスしてください。"); 31 sb.AppendLine("テラテイルはhttps://teratail.com/こちらです。"); 32 var text = sb.ToString(); 33 34 // 置換処理 35 text = regexUrlOnly.Replace(text, "<a href=\"${url}\">${url}</a>"); 36 System.Diagnostics.Trace.TraceInformation("Url Only: {0}", text); 37 text = regexMarkdown.Replace(text, "<a href=\"${url}\">${text}</a>"); 38 System.Diagnostics.Trace.TraceInformation("Markdown: {0}", text); 39 40 // テスト結果 41 var sbCorrect = new StringBuilder(); 42 sbCorrect.AppendLine("<a href=\"https://www.google.co.jp/\">Google</a>にアクセスしてください。"); 43 sbCorrect.AppendLine("テラテイルは<a href=\"https://teratail.com/\">https://teratail.com/</a>こちらです。"); 44 45 Assert.AreEqual(sbCorrect.ToString(), text); 46} 47

実装例2: 1発で置換を行うパターン

cs

1[TestMethod] 2public void TestTeratail356904_2() 3{ 4 var expressionUrl = "s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+"; 5 6 // グループ化(置換でこの部分を抜き出すため) 7 var expressionUrlGroup = "(?<url>" + expressionUrl + ")"; 8 9 // 正規表現 URLのみ: 正規表現の前方否定戻り読み「(?<!~)」でカッコやダブルクォーテーションがマッチしないようにする 10 var expressionUrlOnly = "(?<![\(\\"]\s*)" + expressionUrlGroup + "(?!\s*[\)\\"])"; 11 // ^^^^^^^ カッコやダブルクォーテーション 12 // ^^^^ スペースを入れた場合の考慮 13 14 // 正規表現 マークダウン 15 var expressionMarkdown = "\[(?<text>[^\]]*)\]\(\s*" + expressionUrlGroup + "\s*\)"; 16 17 // 正規表現 全て 18 var expressonAll = "(" + expressionUrlOnly + ")|(" + expressionMarkdown + ")"; 19 20 var regexUrl = new Regex(expressonAll, 21 RegexOptions.IgnoreCase // 大文字小文字を区別しない 22 | RegexOptions.Compiled // コンパイルする(この正規表現を使いまわす場合) 23 ); 24 25 // 置換元文字列 26 var sb = new StringBuilder(); 27 sb.AppendLine("[Google](https://www.google.co.jp/)にアクセスしてください。"); 28 sb.AppendLine("テラテイルはhttps://teratail.com/こちらです。"); 29 30 // 置換処理 31 var text = regexUrl.Replace(sb.ToString(), (match) => 32 { 33 var foundText = match.Groups["text"].Success; 34 return match.Result(foundText ? "<a href=\"${url}\">${text}</a>" : "<a href=\"${url}\">${url}</a>"); 35 }); 36 37 System.Diagnostics.Trace.TraceInformation("All: {0}", text); 38 39 // テスト結果 40 var sbCorrect = new StringBuilder(); 41 sbCorrect.AppendLine("<a href=\"https://www.google.co.jp/\">Google</a>にアクセスしてください。"); 42 sbCorrect.AppendLine("テラテイルは<a href=\"https://teratail.com/\">https://teratail.com/</a>こちらです。"); 43 44 Assert.AreEqual(sbCorrect.ToString(), text); 45} 46

参考文献

本回答に辿り着く際に参照したリソース。

投稿2021/08/30 16:18

編集2021/08/30 16:40
testset

総合スコア223

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kawauso

2021/08/31 04:54

ご丁寧な回答ありがとうございました。理解しやすかった実装例1で実装を行いました。 正規表現の名前付きグループや前方否定戻り読みなど知らなかった手法ばかりで大変参考になりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問