URL'ler için Path.Combine?


1243

Path.Combine kullanışlıdır, ancak URL'ler için .NET çerçevesinde benzer bir işlev var mı?

Ben böyle bir sözdizimi arıyorum:

Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")

hangi dönecekti:

"http://MyUrl.com/Images/Image.jpg"


14
FlurlUrl.Combine bunu yapan bir yöntem içerir .
Todd Menier

2
Aslında, // tarayıcı tarafından değil, web sitesinin veya sunucunun yönlendirilmesi tarafından işlenir. Adres çubuğuna koyduklarınızı gönderir. Bu yüzden http: // yerine htp: // yazdığımızda sorun yaşıyoruz. Böylece // bazı sitelerde büyük sorunlara neden olabilir. Ben url // varsa 404 atar belirli bir web sitesini işleyen bir tarayıcı için bir .dll yazıyorum.
Dave Gordon

Yanıtlar:


73

Orada üstünde bir Todd Menier en yorumdur o Flurl bir içermektedirUrl.Combine .

Daha fazla detay:

Url.Combine temelde URL'ler için bir Path.Combine'dir ve parçalar arasında bir ve sadece bir ayırıcı karakter sağlar:

var url = Url.Combine(
    "http://MyUrl.com/",
    "/too/", "/many/", "/slashes/",
    "too", "few?",
    "x=1", "y=2"
// result: "http://www.MyUrl.com/too/many/slashes/too/few?x=1&y=2" 

NuGet'te Flurl.Http alın :

PM> Kurulum Paketi Flurl.Http

Veya HTTP özellikleri olmadan bağımsız URL oluşturucu edinin :

PM> Yükleme Paketi Flurl


4
Bu soru çok fazla trafik alıyor ve 1000+ upvotes ile cevap aslında her durumda işe yaramıyor. Yıllar sonra, aslında bunun için Flurl kullanıyorum, bu yüzden bunu kabul ediyorum. Karşılaştığım tüm durumlarda işe yarıyor gibi görünüyor. İnsanlar bir bağımlılık almak istemiyorsa, aynı zamanda iyi çalışan bir cevap yayınladım.
Brian MacKay

ve kullanmazsanız Flurlve hafif bir versiyonunu görürseniz
lizzy91

1157

Uri bunu sizin için yapması gereken bir kurucuya sahiptir: new Uri(Uri baseUri, string relativeUri)

İşte bir örnek:

Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");

Editörden not: Dikkat, bu yöntem beklendiği gibi çalışmıyor. Bazı durumlarda baseUri'nin bir kısmını kesebilir. Yorumlara ve diğer yanıtlara bakın.


369
Uri sınıfının kullanımını seviyorum, maalesef OP'nin istediği gibi Path.Combine gibi davranmayacak. Örneğin yeni Uri (yeni Uri (" test.com/mydirectory/" ), "/helloworld.aspx"). ToString () size " test.com/helloworld.aspx " verir ; Path.Combine tarzı bir sonuç istiyorsak bu yanlış olur.
Doktor Jones

195
Hepsi eğik çizgilerde. Göreli yol kısmı bir eğik çizgi ile başlarsa, açıkladığınız gibi davranır. Ancak, eğik çizgiyi dışarıda bırakırsanız, beklediğiniz şekilde çalışır (ikinci parametrede eksik eğik çizgiye dikkat edin): yeni Uri (yeni Uri (" test.com/mydirectory/" ), "helloworld.aspx" ) .ToString (), " test.com/mydirectory/helloworld.aspx " ile sonuçlanır . Path.Combine benzer şekilde davranır. Göreli yol parametresi eğik çizgi ile başlarsa, yalnızca göreceli yolu döndürür ve birleştirmez.
Joel Beckham

70
BaseUri'niz "test.com/mydirectory/mysubdirectory" olsaydı, sonuç "test.com/mydirectory/mysubdirectory/helloworld.aspx" yerine "test.com/mydirectory/helloworld.aspx" olur. Küçük fark, ilk parametrede eğik çizgi olmamasıdır. Mevcut çerçeve yöntemlerini kullanmak için her şeyim, eğer zaten orada eğik çizgi varsa, o zaman ben partUrl1 + partUrl2 yapmanın çok daha az kokuyor düşünüyorum - Ben potansiyel bir süre boyunca eğik çizgi yuvarlak peşinde olabilirdi string concat yapmama uğruna.
Carl

64
Bir URI birleştirme yöntemi istemem tek nedeni, sonunda eğik çizgi kontrol etmek zorunda değilsiniz. Uygulamanız kökteyse Request.ApplicationPath '/', eğer değilse '/ foo' olur.
nickd

24
Ben -1 bu cevabı çünkü bu soruna cevap vermiyor. URL'yi birleştirmek istediğinizde, Path.Combine kullanmak istediğinizde olduğu gibi, sondaki / ile ilgilenmek istemezsiniz. ve bununla ilgilenmelisin. Ben Brian MacKay veya yukarıdaki mdsharpe çözüm tercih
Baptiste

161

Bu uygun bir çözüm olabilir:

public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}

7
+1: Bu, göreli stil yollarını (../../whatever.html) ele almasa da, basitliği için bunu beğendim. Ayrıca '\' karakteri için kırpma eklerdim.
Brian MacKay

3
Bunun daha eksiksiz bir versiyonu için cevabımı görün.
Brian MacKay

149

Siz kullanın Uri.TryCreate( ... ):

Uri result = null;

if (Uri.TryCreate(new Uri("http://msdn.microsoft.com/en-us/library/"), "/en-us/library/system.uri.trycreate.aspx", out result))
{
    Console.WriteLine(result);
}

Geri dönücek:

http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx


53
+1: Çıktı parametresiyle ilgili irrasyonel bir sorunum olmasına rağmen bu iyi. ;)
Brian MacKay

10
@Brian: yardımcı olursa, tüm TryXXX yöntemlerinde ( int.TryParse, DateTime.TryParseExact) bir if ifadesinde kullanılmasını kolaylaştırmak için bu çıktı parametresi bulunur. Btw, Ryan'ın bu örnekte yaptığı gibi değişkeni başlatmanız gerekmez.
Abel

41
Bu cevap aynı sorun uğrar Joel : katılmadan test.com/mydirectory/ve /helloworld.aspxsonuçlanacaktır test.com/helloworld.aspxistediğini görünüşte olmadığı.
Matt Kocaj

3
Merhaba, bu takip için başarısız oldu: if (Uri.TryCreate (yeni Uri (" localhost / MyService /" ), "/ Event / SomeMethod? Abc = 123", sonuç)) {Console.WriteLine (sonuç); } Bu bana sonucu şöyle gösteriyor: localhost / Event / SomeMethod? Abc = 123 Not: "http: //", burada
Uover

3
@FaisalMq Bu, köke göre ikinci bir parametre geçtiğiniz için doğru davranıştır. İkinci parametrede önde / dışında kalsaydınız, beklediğiniz sonucu elde edersiniz.
Tom Lint

127

Burada zaten bazı harika cevaplar var. Mdsharpe önerisine dayanarak, Uri örnekleriyle uğraşmak istediğinizde kolayca kullanılabilecek bir uzantı yöntemi:

using System;
using System.Linq;

public static class UriExtensions
{
    public static Uri Append(this Uri uri, params string[] paths)
    {
        return new Uri(paths.Aggregate(uri.AbsoluteUri, (current, path) => string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/'))));
    }
}

Ve kullanım örneği:

var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;

Bu http://example.com/subpath/part1/part2 üretecektir


2
Bu çözüm, Path.Combine () yöntemine çok benzeyen bir UriUtils.Combine ("base url", "part1", "part2", ...) statik yöntem yazmayı önemsiz kılar. Güzel!
angularsen

Göreli URI'ları desteklemek için Uri yapıcısında AbsoluteUri ve UriKind.AbsoluteOrRelative yerine ToString () kullanmak zorunda kaldım.
angularsen

Göreli Uris hakkındaki bahşiş için teşekkürler. Ne yazık ki Uri göreceli yollarla başa çıkmayı kolaylaştırmaz, çünkü Request.ApplicationPath ile ilgili her zaman biraz mucking vardır. Belki de yeni Uri'yi (HttpContext.Current.Request.ApplicationPath) bir üs olarak kullanmayı deneyebilir ve bunun üzerine Append'i çağırabilirsiniz. Bu size mutlak yollar verecektir, ancak site yapısı içinde herhangi bir yerde çalışmalıdır.
Ales Potocnik Hahonina

Harika. Başka birine yardım ettiğine sevindim. Bunu bir süredir kullanıyorum ve herhangi bir sorun yaşamadım.
Ales Potocnik Hahonina

Ben de eklemek için yollardan herhangi birinin boş veya boş dize olmadığını kontrol ekledi.
n.podbielski

92

Ryan Cook'un cevabı peşinde olduğum şeye yakın ve diğer geliştiriciler için daha uygun olabilir. Ancak, dizenin başına http: // ekler ve genel olarak ben sonradan biraz daha biçimlendirme yapar.

Ayrıca, benim kullanım durumlarım için, göreli yolları çözmek önemli değildir.

mdsharp'ın cevabı da iyi bir fikrin tohumunu içerir, ancak gerçek uygulamanın tamamlanması için birkaç ayrıntı daha gerekiyordu. Bu, onu ortadan kaldırmak için bir girişimdir (ve bunu üretimde kullanıyorum):

C #

public string UrlCombine(string url1, string url2)
{
    if (url1.Length == 0) {
        return url2;
    }

    if (url2.Length == 0) {
        return url1;
    }

    url1 = url1.TrimEnd('/', '\\');
    url2 = url2.TrimStart('/', '\\');

    return string.Format("{0}/{1}", url1, url2);
}

VB.NET

Public Function UrlCombine(ByVal url1 As String, ByVal url2 As String) As String
    If url1.Length = 0 Then
        Return url2
    End If

    If url2.Length = 0 Then
        Return url1
    End If

    url1 = url1.TrimEnd("/"c, "\"c)
    url2 = url2.TrimStart("/"c, "\"c)

    Return String.Format("{0}/{1}", url1, url2)
End Function

Bu kod, VB'de olan aşağıdaki testi geçer:

<TestMethod()> Public Sub UrlCombineTest()
    Dim target As StringHelpers = New StringHelpers()

    Assert.IsTrue(target.UrlCombine("test1", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("/test1/", "/test2/") = "/test1/test2/")
    Assert.IsTrue(target.UrlCombine("", "/test2/") = "/test2/")
    Assert.IsTrue(target.UrlCombine("/test1/", "") = "/test1/")
End Sub

4
Detaylardan bahsetmek: ArgumentNullException("url1")argüman zorunlu ise ne olacak Nothing? Üzgünüm, sadece seçici olmak ;-). Ters eğik çizginin bir URI'de hiçbir ilgisi olmadığını (ve varsa, kesilmemesi gerektiğini) unutmayın, böylece TrimXXX'inizden kaldırabilirsiniz.
Abel

4
params string [] kullanabilir ve 2'den fazla kombinasyona izin vermek için bunları tekrar tekrar birleştirebilirsiniz
Jaider

4
Tabii bu Path.Combine gibi Base Class Kütüphanesi'nde olsaydı.
Uriah Blatherwick

1
@MarkHurd Kodu tekrar düzenledim, böylece davranışsal olarak C # ile aynı ve sözdizimsel olarak eşdeğer.
JJS

1
@BrianMacKay kırdı, markhurd hatamı gösterdi ve geri döndü, tekrar güncelledim ... şerefe
JJS

36

Path.Combine benim için çalışmıyor çünkü "|" gibi karakterler olabilir QueryString argümanlarında ve dolayısıyla bir ArgumentException özelliğine neden olacak URL'de.

İlk Uri(Uri baseUri, string relativeUri)olarak URI'ler nedeniyle benim için başarısız olan yeni yaklaşımı denedim http://www.mediawiki.org/wiki/Special:SpecialPages:

new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")

sonra iki nokta üst üste nedeniyle Special: SpecialPages ile sonuçlanır Special bir şemayı gösterir.

Sonunda mdsharpe / Brian MacKays rotasını kullanmak zorunda kaldım ve birden fazla URI parçası ile çalışmak için biraz daha geliştirdim:

public static string CombineUri(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Length > 0)
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }
    return uri;
}

Kullanımı: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")


1
+1: Şimdi konuşuyoruz ... Bunu deneyeceğim. Bu yeni kabul edilen cevap bile olabilir. Yeni Uri () yöntemini denedikten sonra gerçekten sevmiyorum. Çok titiz.
Brian MacKay

Tam da ihtiyacım olan şey bu! Sondaki eğik çizgiler, vb koymak nereye bakım için bir hayranı değildi ...
Gromer

Boş kontrolde yuvarlanma için +1, böylece patlamaz.
NightOwl888

Count () Uzunluk olmalıdır, böylelikle Linq'i sadece kütüphanenize eklemenize gerek kalmaz.
PRMan

Tam da aradığım şey buydu.
ThePeter

34

Sağladığınız örnek URL'ye dayanarak, sitenize göre URL'leri birleştirmek istediğinizi varsayacağım.

Bu varsayımı temel alarak, bu çözümü sorunuza en uygun yanıt olarak önereceğim: "Path.Combine kullanışlı, URL'ler çerçevesinde benzer bir işlev var mı?"

Bir olmadığı için benzer işlevi "VirtualPathUtility.Combine" yöntemi: URL'ler için çerçevesinde ben doğru olduğunu öneriyorum. MSDN referans bağlantısı: şöyledir VirtualPathUtility.Combine Method

Bir uyarı var: Bunun yalnızca sitenize göre URL'ler için çalıştığına inanıyorum (yani, başka bir web sitesine bağlantı oluşturmak için kullanamazsınız. Örneğin var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");).


+1, çünkü aradığım şeye yakın, ancak eski bir URL için işe yarayacaksa ideal olurdu. Ben mdsharpe teklif ne çok daha zarif alacak iki katına.
Brian MacKay

2
Uyarı doğrudur, mutlak uris ile çalışamaz ve sonuç her zaman kökten görecelidir. Ama ek bir yararı vardır, "~ /" gibi, tilde işler. Bu onu Server.MapPathbirleştirme ve birleştirme kısayolu yapar .
Abel

25
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

12
path.Replace(Path.DirectorySeparatorChar, '/');
Jaider

5
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja - MSFT

1
Onu yazmak için u ilk / ikinci arg yani "/ Görüntüler" - / Path.Combine (" Http://MyUrl.com ", "Görüntüler / Image.jpg")
Per G

8
@SliverNinja Bu doğru değil Bu alanın değeri UNIX için ters eğik çizgi ('\') ve Windows ve Macintosh işletim sistemlerinde eğik çizgi ('/'). Mono'yu bir Linux sisteminde kullanırken, yanlış ayırıcıyla karşılaşırsınız.
user247702

6
Dizin Ayırıcı dışarı geeking tüm yall dizeleri şu anda olduğundan farklı bir işletim sistemi gelmiş olabilir unutuyor. Ters eğik çizgiyi ileri eğik çizgi ile değiştirin ve üzeresiniz.
JeremyWeir

17

Sadece küçük bir uzantı yöntemi bir araya getirdim:

public static string UriCombine (this string val, string append)
        {
            if (String.IsNullOrEmpty(val)) return append;
            if (String.IsNullOrEmpty(append)) return val;
            return val.TrimEnd('/') + "/" + append.TrimStart('/');
        }

Bu şekilde kullanılabilir:

"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");

12

Esprili bir örnek olan Ryan, işleve bir bağlantı ile bitiyor. Aferin.

Bir öneri Brian: Bu kodu bir işlevde sararsanız, TryCreate çağrısından önce temel URL'yi sarmak için bir UriBuilder kullanmak isteyebilirsiniz.

Aksi takdirde, temel URL şemayı içermelidir ZORUNLU (burada UriBuilder http: // varsayar). Sadece bir düşünce:

public string CombineUrl(string baseUrl, string relativeUrl) {
    UriBuilder baseUri = new UriBuilder(baseUrl);
    Uri newUri;

    if (Uri.TryCreate(baseUri.Uri, relativeUrl, out newUri))
        return newUri.ToString();
    else
        throw new ArgumentException("Unable to combine specified url values");
}

10

Onları birleştirmenin ve her zaman doğru olmasını sağlamanın kolay bir yolu:

string.Format("{0}/{1}", Url1.Trim('/'), Url2);

+1, mdsharpe'ın cevabına çok benzese de, cevabımda geliştirdim. Url2 / veya \ ile başlamazsa veya Url1 yanlışlıkla \ ile bitmezse veya biri boş değilse bu sürüm harika çalışır! :)
Brian MacKay

9

Bir URL'nin birden çok bölümünü birleştirmek biraz zor olabilir. İki parametreli yapıcıyı Uri(baseUri, relativeUri)veya Uri.TryCreate()yardımcı program işlevini kullanabilirsiniz .

Bu yöntemler birinci parametrenin kapalı göreli parçalar kesiliyor devam çünkü iki durumda da, hatalı bir sonuç dönen sonunda olabilir baseUrigibi bir şey yani http://google.com/some/thingiçin http://google.com.

Birden çok parçayı nihai bir URL'de birleştirmek için aşağıdaki iki işlevi kopyalayabilirsiniz:

    public static string Combine(params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;

        var urlBuilder = new StringBuilder();
        foreach (var part in parts)
        {
            var tempUrl = tryCreateRelativeOrAbsolute(part);
            urlBuilder.Append(tempUrl);
        }
        return VirtualPathUtility.RemoveTrailingSlash(urlBuilder.ToString());
    }

    private static string tryCreateRelativeOrAbsolute(string s)
    {
        System.Uri uri;
        System.Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out uri);
        string tempUrl = VirtualPathUtility.AppendTrailingSlash(uri.ToString());
        return tempUrl;
    }

Kullanımı göstermek için birim testleri içeren tam kodu https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs adresinde bulabilirsiniz.

En yaygın üç vakayı kapsayan birim testlerim var:

Resim açıklamasını buraya girin


2
Bana oldukça iyi gözüküyor. Daha iyi netlik için I döngüsünü bir foreach döngüsüyle değiştirebilmenize rağmen.
Chris Marisic

Teşekkürler Chris. Foreach kullanmak için kodumu değiştirdim.
İnanıyorum2014

1
Tüm ekstra çaba için +1. Daha yüksek oylanan cevapların bazıları için bu soruyu biraz korumam gerekiyor, sen dayağı attın. ;)
Brian MacKay

Üzgünüm, ama yeterli değil. Gösterdiğiniz birkaç durumda çalışır, ancak tüm kombinasyonlarda kullanılabilir olmaktan uzaktır. Örneğin, yoldaki iki nokta üst üste zarar verir.
Gábor

Ne demek istediğinize bir örnek verebilir misiniz? Sorunu düzeltmekten ve sonraki kullanıcılara yardımcı olmaktan memnuniyet duyarız.
Believe2014

7

UriBuilderBu tür şeyler için gerçekten iyi iş buldum :

UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;

Daha fazla kurucu ve dokümantasyon için UriBuilder Sınıfı - MSDN'ye bakın .


4

İşte Microsoft'un (OfficeDev PnP) yöntemi UrlUtility.Combine :

    const char PATH_DELIMITER = '/';

    /// <summary>
    /// Combines a path and a relative path.
    /// </summary>
    /// <param name="path"></param>
    /// <param name="relative"></param>
    /// <returns></returns>
    public static string Combine(string path, string relative) 
    {
        if(relative == null)
            relative = String.Empty;

        if(path == null)
            path = String.Empty;

        if(relative.Length == 0 && path.Length == 0)
            return String.Empty;

        if(relative.Length == 0)
            return path;

        if(path.Length == 0)
            return relative;

        path = path.Replace('\\', PATH_DELIMITER);
        relative = relative.Replace('\\', PATH_DELIMITER);

        return path.TrimEnd(PATH_DELIMITER) + PATH_DELIMITER + relative.TrimStart(PATH_DELIMITER);
    }

Kaynak: GitHub


Görünüşe göre bu URL'ler yerine yollar için olabilir.
Brian MacKay

@BrianMacKay Buna benzediğini kabul etti, ancak UrlUtility sınıfından geliyor ve URL'leri birleştirme bağlamında kullanılıyor

2
Hangi sınıfa ait olduğunu açıklığa kavuşturmak için düzenlendi

Bu Sınıfı kullanırken dikkatli olun, sınıfın geri kalanında SharePoint'e özgü yapay öğeler bulunur.
Harry Berry

4

Aşağıdakileri yararlı buluyorum ve aşağıdaki özelliklere sahibim:

  • Boş veya beyaz boşlukta atar
  • Birden paramsçok URL segmenti için birden fazla parametre alır
  • boş veya boş atar

Sınıf

public static class UrlPath
{
   private static string InternalCombine(string source, string dest)
   {
      if (string.IsNullOrWhiteSpace(source))
         throw new ArgumentException("Cannot be null or white space", nameof(source));

      if (string.IsNullOrWhiteSpace(dest))
         throw new ArgumentException("Cannot be null or white space", nameof(dest));

      return $"{source.TrimEnd('/', '\\')}/{dest.TrimStart('/', '\\')}";
   }

   public static string Combine(string source, params string[] args) 
       => args.Aggregate(source, InternalCombine);
}

Testler

UrlPath.Combine("test1", "test2");
UrlPath.Combine("test1//", "test2");
UrlPath.Combine("test1", "/test2");

// Result = test1/test2

UrlPath.Combine(@"test1\/\/\/", @"\/\/\\\\\//test2", @"\/\/\\\\\//test3\") ;

// Result = test1/test2/test3

UrlPath.Combine("/test1/", "/test2/", null);
UrlPath.Combine("", "/test2/");
UrlPath.Combine("/test1/", null);

// Throws an ArgumentException

Düzenlemek için @PeterMortensen teşekkürler
thegeneral

Testlerle ilgili bazı sorunlar: 4. Sonuç = test1 / test2 / test3 \ dördüncü ve son atış testleri için ArgumentException yerine ArgumentNullException veriyor
Moriya

3

Genel çözümüm:

public static string Combine(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Any())
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);

        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }

    return uri;
}

Bu yardımcı yöntem çok esnektir ve birçok farklı kullanım durumunda iyi çalışır. Teşekkür ederim!
Shiva

3

Hayatınızı kolaylaştıracak bu işlevi yarattım:

    /// <summary>
    /// The ultimate Path combiner of all time
    /// </summary>
    /// <param name="IsURL">
    /// true - if the paths are Internet URLs, false - if the paths are local URLs, this is very important as this will be used to decide which separator will be used.
    /// </param>
    /// <param name="IsRelative">Just adds the separator at the beginning</param>
    /// <param name="IsFixInternal">Fix the paths from within (by removing duplicate separators and correcting the separators)</param>
    /// <param name="parts">The paths to combine</param>
    /// <returns>the combined path</returns>
    public static string PathCombine(bool IsURL , bool IsRelative , bool IsFixInternal , params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;
        char separator = IsURL ? '/' : '\\';

        if (parts.Length == 1 && IsFixInternal)
        {
            string validsingle;
            if (IsURL)
            {
                validsingle = parts[0].Replace('\\' , '/');
            }
            else
            {
                validsingle = parts[0].Replace('/' , '\\');
            }
            validsingle = validsingle.Trim(separator);
            return (IsRelative ? separator.ToString() : string.Empty) + validsingle;
        }

        string final = parts
            .Aggregate
            (
            (string first , string second) =>
            {
                string validfirst;
                string validsecond;
                if (IsURL)
                {
                    validfirst = first.Replace('\\' , '/');
                    validsecond = second.Replace('\\' , '/');
                }
                else
                {
                    validfirst = first.Replace('/' , '\\');
                    validsecond = second.Replace('/' , '\\');
                }
                var prefix = string.Empty;
                if (IsFixInternal)
                {
                    if (IsURL)
                    {
                        if (validfirst.Contains("://"))
                        {
                            var tofix = validfirst.Substring(validfirst.IndexOf("://") + 3);
                            prefix = validfirst.Replace(tofix , string.Empty).TrimStart(separator);

                            var tofixlist = tofix.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                            validfirst = separator + string.Join(separator.ToString() , tofixlist);
                        }
                        else
                        {
                            var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                            validfirst = string.Join(separator.ToString() , firstlist);
                        }

                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                    else
                    {
                        var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                        validfirst = string.Join(separator.ToString() , firstlist);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                }
                return prefix + validfirst.Trim(separator) + separator + validsecond.Trim(separator);
            }
            );
        return (IsRelative ? separator.ToString() : string.Empty) + final;
    }

URL'ler ve normal yollar için çalışır.

Kullanımı:

    // Fixes internal paths
    Console.WriteLine(PathCombine(true , true , true , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: /folder 1/folder2/folder3/somefile.ext

    // Doesn't fix internal paths
    Console.WriteLine(PathCombine(true , true , false , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : /folder 1//////////folder2////folder3/somefile.ext

    // Don't worry about URL prefixes when fixing internal paths
    Console.WriteLine(PathCombine(true , false , true , @"/\/\/https:/\/\/\lul.com\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: https://lul.com/folder2/folder3/somefile.ext

    Console.WriteLine(PathCombine(false , true , true , @"../../../\\..\...\./../somepath" , @"anotherpath"));
    // Result: \..\..\..\..\...\.\..\somepath\anotherpath

3

Neden sadece aşağıdakileri kullanmıyorsunuz?

System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")

Ben olacağını bunun PowerShell sürümü arıyordu: [System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")ancak bu sonucu başarısız: /Images/Image.jpg. Kaldır /ikinci Alt yol gelen ve çalışır:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Underverse

Güzel fikir, ancak parametrelerden biri boş olduğunda başarısız olur.
pholpar

2

URL'leri bir URI ile birleştirirken kurallar

Garip davranışlardan kaçınmak için uyulması gereken bir kural vardır:

  • Yol (dizin) '/' ile bitmelidir. Yol '/' olmadan bitiyorsa, son bölüm bir dosya adı gibi ele alınır ve sonraki URL bölümü ile birleştirilmeye çalışılırken birleştirilir.
  • Bir istisna vardır: temel URL adresinin (dizin bilgisi olmadan) '/' ile bitmesi gerekmez
  • yol kısmı '/' ile başlamamalıdır. '/' İle başlıyorsa, URL'deki mevcut her göreli bilgi kaldırılır ... bir string.Emptyparça yolu eklemek göreli dizini URL'den de kaldırır!

Yukarıdaki kurallara uyarsanız, URL'leri aşağıdaki kodla birleştirebilirsiniz. Durumunuza bağlı olarak, URL'ye birden çok 'dizin' parçası ekleyebilirsiniz ...

        var pathParts = new string[] { destinationBaseUrl, destinationFolderUrl, fileName };

        var destination = pathParts.Aggregate((left, right) =>
        {
            if (string.IsNullOrWhiteSpace(right))
                return left;

            return new Uri(new Uri(left), right).ToString();
        });

2

Eğer ASP.NET Çekirdek (ayrıca Microsoft.Owin mevcuttur) içinde, Flurl gibi bir üçüncü taraf bağımlılık böyle ekleyebilir veya özel bir uzantısı yöntemi oluşturmak istemiyorsanız, kullanabileceğiniz PathStringURI kadar bina amacıyla tasarlanmıştır hangi yolları. Daha sonra bunun bir kombinasyonunu kullanarak tam URI'nizi oluşturabilir UriveUriBuilder .

Bu durumda şöyle olur:

new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())

Bu, temel URL'deki ayırıcıları belirtmek zorunda kalmadan tüm bileşen parçalarını verir. Ne yazık ki, PathStringgerektirir /her dize eklenir aksi takdirde aslında bir atar ArgumentException! Ama en azından URI'nizi belirleyici olarak kolayca birim testine tabi tutulacak şekilde oluşturabilirsiniz.


2

Yani UriBuilder kullanan herkese benzer başka bir yaklaşımım var.

BaseUrl (yolun bir kısmını içerebilir - örneğin http://mybaseurl.com/dev/ ) javajavajavajavajava olarak bölmek istemedim .

Aşağıdaki kod parçası, + Testler kodunu gösterir.

Dikkat: Bu çözüm ana bilgisayarı indirir ve bir bağlantı noktası ekler. Bu arzu değilse, bir örneğin kaldıraç tarafından bir dize temsilini yazabilir UriMülkiyeti UriBuilder.

  public class Tests
  {
         public static string CombineUrl (string baseUrl, string path)
         {
           var uriBuilder = new UriBuilder (baseUrl);
           uriBuilder.Path = Path.Combine (uriBuilder.Path, path);
           return uriBuilder.ToString();
         }

         [TestCase("http://MyUrl.com/", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath/", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         public void Test1 (string baseUrl, string path, string expected)
         {
           var result = CombineUrl (baseUrl, path);

           Assert.That (result, Is.EqualTo (expected));
         }
  }

Windows 10'da .NET Core 2.1 ile test edilmiştir.

Bu neden işe yarıyor?

Path.CombineTers eğik çizgiler (en azından Windows'ta) geri dönecek olsa da , UriBuilder bu davayı Setter'da ele alır Path.

Alındığı https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs (çağrısını akla string.Replace)

[AllowNull]
public string Path
{
      get
      {
          return _path;
      }
      set
      {
          if ((value == null) || (value.Length == 0))
          {
              value = "/";
          }
          _path = Uri.InternalEscapeString(value.Replace('\\', '/'));
          _changed = true;
      }
 }

Bu en iyi yaklaşım mı?

Kesinlikle bu çözüm kendini çok iyi tanımlamaktadır (en azından benim görüşüme göre). Ama belgelenmemiş güveniyorsun (en azından hızlı bir google arama ile hiçbir şey bulamadım) .NET API "özelliği". Bu, gelecekteki bir sürümle değişebilir.

Doğru dönüştürülmüş olup olmadığını kontrol eden https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs ( Path_Get_Set) ' de testler vardır \.

Yan Not:UriBuilder.Uri uri bir System.Urictor için kullanılacaksa, doğrudan mülkle de çalışılabilir .


Bu çok güvenilir bir yaklaşım. Birim testi için Yaşasın !!
aggsol

2

Bir astar arayan ve sadece yeni bir yöntem oluşturmadan veya yeni bir kitaplığa başvurmadan veya bir URI değeri oluşturmadan ve bir dizeye dönüştürmek için bir yolun parçalarına katılmak isteyen herkes için, o zaman ...

string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");

Oldukça basit, ama daha fazlasına ihtiyacınız olduğunu göremiyorum. Eğer '/' değerini ikiye katlamaktan korkuyorsanız daha sonra yapabilirsiniz .Replace("//", "/"). 'Https: //' içindeki iki katına çıkmış '//' yerine koymaktan korkuyorsanız, bunun yerine bir katılma yapın, katlanan '/' yerine koyun, ardından web sitesi URL'sine katılın (ancak çoğu tarayıcının otomatik olarak doğru biçimde okumak için önündeki 'https:' ile herhangi bir şeyi dönüştürün). Bu şöyle görünecektir:

string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));

Burada yukarıdakilerin hepsini işleyecek birçok cevap var, ama benim durumumda, sadece bir yerde bir kez ihtiyacım vardı ve buna çok fazla güvenmek zorunda kalmayacağım. Ayrıca, burada neler olduğunu görmek gerçekten çok kolay.

Bkz. Https://docs.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8


1

kullanın:

    private Uri UriCombine(string path1, string path2, string path3 = "", string path4 = "")
    {
        string path = System.IO.Path.Combine(path1, path2.TrimStart('\\', '/'), path3.TrimStart('\\', '/'), path4.TrimStart('\\', '/'));
        string url = path.Replace('\\','/');
        return new Uri(url);
    }

Aynı şekilde davranma avantajına sahiptir Path.Combine.


1

İşte yaklaşımım ve ben de kendim için kullanacağım:

public static string UrlCombine(string part1, string part2)
{
    string newPart1 = string.Empty;
    string newPart2 = string.Empty;
    string seperator = "/";

    // If either part1 or part 2 is empty,
    // we don't need to combine with seperator
    if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
    {
        seperator = string.Empty;
    }

    // If part1 is not empty,
    // remove '/' at last
    if (!string.IsNullOrEmpty(part1))
    {
        newPart1 = part1.TrimEnd('/');
    }

    // If part2 is not empty,
    // remove '/' at first
    if (!string.IsNullOrEmpty(part2))
    {
        newPart2 = part2.TrimStart('/');
    }

    // Now finally combine
    return string.Format("{0}{1}{2}", newPart1, seperator, newPart2);
}

Bu sadece sizin durumunuz için kabul edilebilir. Kodunuzu kırabilecek durumlar var. Ayrıca, yolun parçalarının düzgün kodlamasını yapmadınız. Siteler arası komut dosyası saldırısı söz konusu olduğunda bu büyük bir güvenlik açığı olabilir.
İnan2014

Puanını kabul ediyorum. Kodun sadece iki url parçasını basit bir şekilde birleştirmesi gerekiyor.
Amit Bhagat

1

Bunu kullan:

public static class WebPath
{
    public static string Combine(params string[] args)
    {
        var prefixAdjusted = args.Select(x => x.StartsWith("/") && !x.StartsWith("http") ? x.Substring(1) : x);
        return string.Join("/", prefixAdjusted);
    }
}

'WebPath' ile hoş bir dokunuş. :) Kod gereksiz yere yoğun olabilir - buna bir göz atmak ve evet, bu mükemmel. Ünite testlerini görmek istememi sağlıyor. Belki de sadece benim!
Brian MacKay

1
x.StartsWith ("/") &&! x.StartsWith ("http") - neden http kontrol ediyor? ne kazanıyorsun
penguat

Http ile başlıyorsa eğik çizgiyi kaldırmayı denemek istemezsiniz.
Martin Murphy

@BrianMacKay, iki astarın bir ünite testini garanti ettiğinden emin değilim, ancak isterseniz bunu sağlamaktan çekinmeyin. Yamaları veya herhangi bir şeyi kabul ediyorum gibi değil, öneriyi düzenlemekten çekinmeyin.
Martin Murphy

1

Ben Uriyapıcı '\' '/' içine çevirir bulundu . Böylece yapıcı Path.Combineile de kullanabilirsiniz Uri.

 Uri baseUri = new Uri("http://MyUrl.com");
 string path = Path.Combine("Images", "Image.jpg");
 Uri myUri = new Uri(baseUri, path);

1

Değer için, burada birkaç uzatma yöntemi. Birincisi yolları birleştirecek ve ikincisi URL'ye parametreler ekleyecek.

    public static string CombineUrl(this string root, string path, params string[] paths)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return root;
        }

        Uri baseUri = new Uri(root);
        Uri combinedPaths = new Uri(baseUri, path);

        foreach (string extendedPath in paths)
        {
           combinedPaths = new Uri(combinedPaths, extendedPath);
        }

        return combinedPaths.AbsoluteUri;
    }

    public static string AddUrlParams(this string url, Dictionary<string, string> parameters)
    {
        if (parameters == null || !parameters.Keys.Any())
        {
            return url;
        }

        var tempUrl = new StringBuilder($"{url}?");
        int count = 0;

        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            if (count > 0)
            {
                tempUrl.Append("&");
            }

            tempUrl.Append($"{WebUtility.UrlEncode(parameter.Key)}={WebUtility.UrlEncode(parameter.Value)}");
            count++;
        }

        return tempUrl.ToString();
    }

1

Diğer cevaplarda bulunan, yeni Uri()veya TryCreate()kene yapabilirsiniz. Bununla birlikte, üs Uri ile bitmek zorundadır /ve akraba ile BAŞLAMAK gerekir /; Aksi takdirde, temel URL'nin son kısmını kaldıracak

Bunun en iyi şekilde bir uzantı yöntemi olarak yapıldığını düşünüyorum, yani

public static Uri Append(this Uri uri, string relativePath)
{
    var baseUri = uri.AbsoluteUri.EndsWith('/') ? uri : new Uri(uri.AbsoluteUri + '/');
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri, relative);
}

ve kullanmak için:

var baseUri = new Uri("http://test.com/test/");
var combinedUri =  baseUri.Append("/Do/Something");

Performans açısından, bu çok fazla ayrıştırma ve doğrulama yapan Uri sınıfı nedeniyle ihtiyaç duyduğundan daha fazla kaynak tüketir; çok kaba bir profil oluşturma (Debug) yaklaşık 2 saniyede bir milyon işlem gerçekleştirdi. Bu, çoğu senaryo için işe yarayacaktır, ancak daha verimli olmak için, her şeyi dize olarak değiştirmek daha iyidir, bu 1 milyon işlem için 125 milisaniye sürer. yani

public static string Append(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return baseUri + relative;
}

Yine de bir URI döndürmek istiyorsanız, 1 milyon işlem için yaklaşık 600 milisaniye sürer.

public static Uri AppendUri(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri + relative);
}

Umarım bu yardımcı olur.


1

Bence bu, istediğiniz kadar yol segmentiyle başa çıkabileceğiniz için size daha fazla esneklik vermelidir:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.