【问题标题】:Function to shrink file path to be more human readable缩小文件路径以更易于阅读的功能
【发布时间】:2012-01-11 17:17:24
【问题描述】:

c#中有什么函数可以缩小文件路径吗?

输入:“c:\users\Windows\Downloaded Program Files\Folder\Inside\example\file.txt”

输出:“c:\users\...\example\file.txt”

【问题讨论】:

  • 这是用于 WinForms 还是您只是想要一个较短的字符串? (我问是因为 .NET 仅支持绘图,这仅适用于 WinForms 和图像)。
  • 为什么要取出完整路径?如果是这种情况,用户将无法找到该文件。如果您对此不担心 - 那就根本不显示路径 - 只显示文件名。
  • user946874:不客气。另外,由于您是 StackOverflow 的新手,我想通知您,您可以通过选中答案旁边的勾号来接受对您最有帮助的答案。

标签: c# .net filepath


【解决方案1】:

对我来说,这看起来不太适合人类阅读。反正我觉得没有这样的功能。在 \ 字符上拆分它,只保留前两个插槽和最后两个插槽,就可以了。

类似这样,虽然代码不是很优雅

  string[] splits = path.Split('\\');
  Console.WriteLine( splits[0] + "\\" + splits[1] + "\\...\\" + splits[splits.Length - 2] + "\\" +  splits[splits.Length - 1]);

【讨论】:

  • 我还要在那里添加一个if (splits.Length > 4) 测试。
  • 这绝对不是缩小路径......想想有一个像C:\veryveryveryveryverylong\supersupersuperlong\a\b\c\d.txt这样的路径......你的解决方案究竟能解决什么问题?
【解决方案2】:

如果需要,请根据路径字符串的长度插入省略号,然后使用以下代码:

TextRenderer.MeasureText(path, Font, 
    new System.Drawing.Size(Width, 0),
    TextFormatFlags.PathEllipsis | TextFormatFlags.ModifyString);

它将就地修改path

编辑:小心使用这种方法。它打破了规则,说 .NET 中的字符串是不可变的。其实MeasureText方法的第一个参数不是ref参数,也就是说不能返回新的字符串。相反,现有字符串被更改。处理使用

创建的副本会很小心
string temp = String.Copy(path);

【讨论】:

    【解决方案3】:

    你可以使用类似的东西:

    public string ShrinkPath(string path, int maxLength)
    {
        List<string> parts = new List<string>(path.Split('\\'));
    
        string start = parts[0] + @"\" + parts[1];
        parts.RemoveAt(1);
        parts.RemoveAt(0);
    
        string end = parts[parts.Count-1];
        parts.RemoveAt(parts.Count-1);
    
        parts.Insert(0, "...");
        while(parts.Count > 1 && 
          start.Length + end.Length + parts.Sum(p=>p.Length) + parts.Count > maxLength)
            parts.RemoveAt(parts.Count-1);
    
        string mid = "";
        parts.ForEach(p => mid += p + @"\");
    
        return start+mid+end;
    }
    

    或者只使用 Olivers 解决方案,这更容易;-)。

    【讨论】:

      【解决方案4】:

      Jeff Atwood posted 在他的博客上解决了这个问题,这里是:

      [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
      static extern bool PathCompactPathEx([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
      
      static string PathShortener(string path, int length)
      {
          StringBuilder sb = new StringBuilder();
          PathCompactPathEx(sb, path, length, 0);
          return sb.ToString();
      }
      

      它使用非托管函数PathCompactPathEx 来实现你想要的。

      【讨论】:

      • 神秘的是,这在框架 3.5 中工作得非常糟糕,我更新到 vs 2010 和框架 4,现在我在这条线上遇到了无法调试的崩溃。 (视觉工作室很忙...)切换到CharSet.Ansi“解决”(?)问题。
      • @v.oddou 它也为我崩溃了,直到我看到使用另一个构造函数的the solution of Danielesb = new StringBuilder(length + 1)
      • 当心:这会崩溃(至少在最近的 .NET 框架中),因为 StringBuilder by default allocates 16 characters 通常不足以存储结果。您需要使用 length 或 MAX_PATH = 260 来避免可能的内存损坏。
      【解决方案5】:

      Nasreddine 的答案几乎是正确的。 在您的情况下,只需指定 StringBuilder 的大小:

      [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
      static extern bool PathCompactPathEx(
                             [Out] StringBuilder pszOut, 
                             string szPath, 
                             int cchMax, 
                             int dwFlags);
      
      static string PathShortener(string path, int length)
      {
          StringBuilder sb = new StringBuilder(length + 1);
          PathCompactPathEx(sb, path, length, 0);
          return sb.ToString();
      }
      

      【讨论】:

        【解决方案6】:

        我刚刚遇到了这个问题,因为漫长的路径变得完全眼痛。这是我快速拼凑的内容(注意马虎),但它完成了工作。

        private string ShortenPath(string path, int maxLength)
        {
            int pathLength = path.Length;
        
            string[] parts;
            parts = label1.Text.Split('\\');
        
            int startIndex = (parts.Length - 1) / 2;
            int index = startIndex;
        
            string output = "";
            output = String.Join("\\", parts, 0, parts.Length);
        
            decimal step = 0;
            int lean = 1;
        
            do
            {
                parts[index] = "...";
        
                output = String.Join("\\", parts, 0, parts.Length);
        
                step = step + 0.5M;
                lean = lean * -1;
        
                index = startIndex + ((int)step * lean);
            }
            while (output.Length >= maxLength && index != -1);
        
            return output;
        }
        

        编辑

        以下是 Merlin2001 的更正更新。

        private string ShortenPath(string path, int maxLength)
        {
            int pathLength = path.Length;
        
            string[] parts;
            parts = path.Split('\\');
        
            int startIndex = (parts.Length - 1) / 2;
            int index = startIndex;
        
            String output = "";
            output = String.Join("\\", parts, 0, parts.Length);
        
            decimal step = 0;
            int lean = 1;
        
            while (output.Length >= maxLength && index != 0 && index != -1)
            {
                parts[index] = "...";
        
                output = String.Join("\\", parts, 0, parts.Length);
        
                step = step + 0.5M;
                lean = lean * -1;
        
                index = startIndex + ((int)step * lean);
            }
            // result can be longer than maxLength
            return output.Substring(0, Math.Min(maxLength, output.Length));  
        }
        

        【讨论】:

        • 聪明的主意!但是你总是至少替换路径的一部分,即使它适合总长度。如果路径不适合,即使所有部分都替换为...,您也可能遇到IndexOutOfRangeException。要修复这个问题(并至少保留驱动器号和最后一个目录),我建议将do..while 更改为while 并检查index != 0 &amp;&amp; index &lt; parts.Length - 1 而不仅仅是index != -1。这样您就不会遇到异常,也不会添加多余的省略号。
        • @Merlin2001 非常感谢你,很好!你的建议肯定会改进我的代码。我一定会做出适当的更新。
        • 您应该提倡使用类型别名,而不是使用类型名称。所以int 而不是Int32
        • 对不起,我只是更喜欢它,可能是由于我的背景和个人喜好。旧习难改。为了争论起见,我会在发布代码 sn-ps 以符合大多数人的期望时尽我所能。
        • 编辑为只允许使用 maxLength 个字符(它可能会在不好的地方中断,但不会超过调用者的指定长度)
        【解决方案7】:
            private string ShrinkPath(string path, int maxLength)
            {
                var parts = path.Split('\\');
                var output = String.Join("\\", parts, 0, parts.Length);
                var endIndex = (parts.Length - 1);
                var startIndex = endIndex / 2;
                var index = startIndex;
                var step = 0;
        
                while (output.Length >= maxLength && index != 0 && index != endIndex)
                {
                    parts[index] = "...";
                    output = String.Join("\\", parts, 0, parts.Length);
                    if (step >= 0) step++;
                    step = (step * -1);
                    index = startIndex + step;
                }
                return output;
            }
        

        【讨论】:

        • 漂亮的代码。也很容易修改以使用像素宽度和MeasureString。谢谢。
        【解决方案8】:

        如果您想编写自己的解决方案来解决此问题,请使用内置类,例如:FileInfoDirectory 等...这样可以减少出错的可能性。

        以下代码生成“VS 风格”的缩短路径,如:“C:\...\Folder\File.ext”。

        public static class PathFormatter
        {
            public static string ShrinkPath(string absolutePath, int limit, string spacer = "…")
            {
                if (string.IsNullOrWhiteSpace(absolutePath))
                {
                    return string.Empty;
                }
                if (absolutePath.Length <= limit)
                {
                    return absolutePath;
                }
        
                var parts = new List<string>();
        
                var fi = new FileInfo(absolutePath);
                string drive = Path.GetPathRoot(fi.FullName);
        
                parts.Add(drive.TrimEnd('\\'));
                parts.Add(spacer);
                parts.Add(fi.Name);
        
                var ret = string.Join("\\", parts);
                var dir = fi.Directory;
        
                while (ret.Length < limit && dir != null)
                {
                    if (ret.Length + dir.Name.Length > limit)
                    {
                        break;
                    }
        
                    parts.Insert(2, dir.Name);
        
                    dir = dir.Parent;
                    ret = string.Join("\\", parts);
                }
        
                return ret;
            }
        }
        

        【讨论】:

          【解决方案9】:

          这里几乎所有的答案都通过计算字符来缩短路径字符串。 但是这种方法忽略了每个字符的宽度。

          这些是 30 个“W”字符:

          WWWWWWWWWWWWWWWWWWWWWWWWWWWW

          这些是 30 个“i”字符:

          iiiiiiiiiiiiiiiiiiiiiiiiiiiiii

          如您所见,计算字符并不是很有用。

          而且您无需编写自己的代码,因为 Windows API 自 Windows 95 起就具有此功能。 此功能的名称是“路径省略号”。 Windows API DrawTextW() 有一个标志 DT_PATH_ELLIPSIS 正是这样做的。 在 .NET 框架中,这在 TextRenderer 类中可用(无需使用 PInvoke)。

          有两种使用方法:


          1.) 将路径直接绘制到标签中:

          public class PathLabel : Label
          {
              protected override void OnPaint(PaintEventArgs e)
              {
                  if (AutoSize)
                      throw new Exception("You must set "+Name+".AutoSize = false in VS " 
                                        + "Designer and assign a fix width to the PathLabel.");
          
                  Color c_Fore = Enabled ? ForeColor : SystemColors.GrayText;
                  TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle, c_Fore, 
                                        BackColor, TextFormatFlags.PathEllipsis);
              }
          }
          

          此标签要求您在 Visual Studio 设计器中关闭 AutoEllipsis,并为标签分配一个固定宽度(路径应占据的最大宽度)。

          您甚至可以在 Visual Studio 设计器中看到截断的路径。

          我输入了一条不适合标签的长路径:

          C:\WINDOWS\Installer{40BF1E83-20EB-11D8-97C5-0009C5020658}\ARPPRODUCTICON.exe

          即使在 Visual Studio 设计器中也显示如下:


          2.) 缩短路径而不将其绘制在屏幕上:

          public static String ShortenPath(String s_Path, Font i_Font, int s32_Width)
          {
              TextRenderer.MeasureText(s_Path, i_Font, new Size(s32_Width, 100), 
                                       TextFormatFlags.PathEllipsis | TextFormatFlags.ModifyString);
          
              // Windows inserts a '\0' character into the string instead of shortening the string
              int s32_Nul = s_Path.IndexOf((Char)0);
              if (s32_Nul > 0)
                  s_Path = s_Path.Substring(0, s32_Nul);
              return s_Path;
          }
          

          标志TextFormatFlags.ModifyString 将'\0' 字符插入到字符串中。在 C# 中修改字符串是非常不寻常的。 通常字符串是不可变的。 这是因为底层 API DrawTextW() 以这种方式工作。 但是由于字符串只是被缩短并且永远不会变得更长,因此没有缓冲区溢出的风险。

          以下代码

          String s_Text = @"C:\WINDOWS\Installer{40BF1E83-20EB-11D8-97C5-0009C5020658}\ARPPRODUCTICON.exe";
          s_Text = ShortenPath(s_Text, new Font("Arial", 12), 500);
          

          将导致“C:\WINDOWS\Installer{40BF1E83-20EB-1...\ARPPRODUCTICON.exe”

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-06-08
            • 1970-01-01
            • 2021-02-18
            • 1970-01-01
            • 2014-01-20
            • 2017-07-11
            • 1970-01-01
            相关资源
            最近更新 更多