【问题标题】:How to shorten file paths using ellipses in Ruby?如何在 Ruby 中使用省略号缩短文件路径?
【发布时间】:2019-10-19 14:16:06
【问题描述】:

在 Ruby(不是 Rails)中,是否有一种直接的方法来获取较长的文件路径并将其不重要的部分替换为省略号,以缩短显示时间?

例如:

a/very/long/path/with/plenty/of/characters/that/won't/fit/on/my/screen/easy

变成这样:

a/very/lo.../screen/easy

我需要能够指定最大长度;并且路径的起点和终点应该始终可见。

如果我用头撞它足够长的时间,我可能会找到解决方案,但也许有人知道方法?

【问题讨论】:

  • 如果第一个和最后一个段比最大长度长怎么办?应该赢什么规则?最大长度或路径的开始和结束应该始终可见?
  • 好问题。我想最后一段会比第一段更重要;长度是最重要的。
  • 为什么变成a/very/lo.../screen/easily而不是a/very/long/...reen/easily(即简单地切成两半)——选择“不重要的部分”的规则是什么?
  • 这只是一个例子,我没有具体计算字符。很可能是其中任何一个。也许我应该更具体。

标签: ruby string path truncate


【解决方案1】:

有点像这样:

shortener = ->(path, length) {
  l = length / 2 - 1
  [path[0...l], path[-l..-1]].join('..')
}
shortener.(path, 10)
#⇒ "a/ve..sily"

【讨论】:

  • 不错。这非常简单。尽管可以进行潜在的改进(例如确保省略号仅直接出现在路径分隔符之前或之后,除非没有足够的空间这样做),但可能不值得付出额外的努力来实现。谢谢,我会在我的程序中试试这个。
  • 如果length 是奇怪的,你隐藏了一个额外的字符
  • @Panic 这就是为什么我用“有点像那样”作为开头的原因。准确的实施取决于许多因素和客户的意愿;以上只是准系统。
  • @SodAlmighty 如果您想在路径分隔符上进行拆分,您可能希望从开头和结尾分别path.split($/)take_while,直到总长度接近所需的长度。跨度>
  • @AlekseiMatiushkin 好主意。
【解决方案2】:

我使用左指针和右指针(lt_ptrrt_ptr)来构建要保留并用省略号分隔的字符串的两个部分。

我首先将右指针减少到最后一个正斜杠,然后将左指针增加到第一个正斜杠,然后将右指针减少到倒数第二个正斜杠,依此类推,直到进一步移动指针会使包含省略号的字符串长于最大允许长度。

代码

def shorten_string(str, max_len, ellipsis = '...')
  return str if str.size <= max_len
  max_len -= ellipsis.size
  ops = [{ index: :index, ptr: 0, chg: 1 },
         { index: :rindex, ptr: str.size-1, chg: -1 } ].cycle
  op = ops.next
  success = true
  loop do
    op = ops.next
    ptr = str.public_send(op[:index], '/', op[:ptr] + op[:chg] )
    lptr = ptr
    rptr = ops.peek[:ptr]
    lptr, rptr = rptr, lptr if op[:index] == :rindex
    if lptr + 1 + str.size - rptr <= max_len
      op[:ptr] = ptr
    else
      break unless success
      success = false
    end        
  end
  op = ops.next if op[:index] == :rindex
  "%s%s%s" % [str[0..op[:ptr]], ellipsis, str[ops.peek[:ptr]..-1]]
end

示例

str = 'a/very/long/path/with/too/many/characters/to/fit/on/my/screen/easily'
shorten_string(str, 40)
  #=> "a/very/long/path/.../on/my/screen/easily" (length: 40)
shorten_string(str, 30)
  #=> "a/very/.../on/my/screen/easily"           (length: 30)
shorten_string(str, 20)
  #=> "a/.../screen/easily"                      (length: 19) 

在前两个示例中,结果字符串的长度(带有省略号)恰好等于最大长度max_length,这当然是巧合。请注意,在第二个示例中,省略号后面有 4 个正斜杠,之前只有 2 个。那是因为在添加"/my" 之后,只能再添加 3 个字符,对于 "long/"(在 "very/" 之后)来说不够,但对于 "/on" 来说已经足够了。

说明

这使用了String#indexString#rindex 的形式,它们采用了第二个参数。

为了更好地理解正在执行的计算,我建议在使用 puts 语句对代码进行加盐之后,针对示例运行代码。修改后的方法示例如下。

def shorten_string(str, max_len, ellipsis = '...')
  return str if str.size <= max_len
  max_len -= ellipsis.size
  puts "str.size=#{str.size}"                                   #!!!!
  ops = [{ index: :index, ptr: 0, chg: 1 },
         { index: :rindex, ptr: str.size-1, chg: -1 } ].cycle
  op = ops.next
  success = true
  loop do
    op = ops.next
    puts "\nop=#{op}, ops.peek=#{ops.peek}"                     #!!!!
    ptr = str.public_send(op[:index], '/', op[:ptr] + op[:chg] )
    lptr = ptr
    rptr = ops.peek[:ptr]
    puts "ptr=#{ptr}, lptr=#{lptr}, rptr=#{rptr}"               #!!!!
    lptr, rptr = rptr, lptr if op[:index] == :rindex
    puts "after possible flip, lptr=#{lptr}, rptr=#{rptr}"      #!!!!
    puts "lptr + 1 + str.size - rptr = #{lptr+1+str.size-rptr}" #!!!!
    if lptr + 1 + str.size - rptr <= max_len
      op[:ptr] = ptr
      puts "after lptr+1+str.size-rptr <= max_len, op=#{op}"    #!!!!
    else
      break unless success
      success = false
    end        
  end
  puts "after loop op=#{op}, ops.peek=#{ops.peek}"              #!!!!
  op = ops.next if op[:index] == :rindex
  "%s%s%s" % [str[0..op[:ptr]], ellipsis, str[ops.peek[:ptr]..-1]]
end

【讨论】:

  • 它看起来有点像 Fortran,但输出看起来很不错。
  • @Eric,Fortran II 还是 IV?我不能不同意。也许我应该将字符串分解成一个数组,然后处理它并加入,但我通常喜欢处理字符串而不这样做。
  • 非常好。可惜它用省略号代替了整个段,而不是截断段,但仍然是一段不错的代码。
  • 我正在玩弄重复切断路径的最后一段和第一段直到剩余长度不足的想法,然后连接剩余部分,截断它,并添加省略号。我得试试看。
  • Sod,关于您的第一条评论,我明白我误读了这个问题。如果不需要在整个片段上打断,这要容易得多
【解决方案3】:
ELLIPSIS = '...'
SEPARATOR = '\\'


def truncate_at_middle_of_filename(filename: str, maxlength: int):
    # nothing to do, already fits
    print(f'Original : {filename}   length {len(filename)}')
    if len(filename) <= maxlength:
        return filename
    segments = filename.split(SEPARATOR)

    return _remove_segment_one_by_one(segments, maxlength)


def _remove_segment_one_by_one(segments: list, maxlength: int):
    newstring = SEPARATOR.join(segments)
    while len(newstring) > maxlength:
        splitpoint = len(segments) // 2
        try:
            segments[splitpoint] = ELLIPSIS
            newstring = SEPARATOR.join(segments)
            del segments[splitpoint]
        except:
            return ""

    return newstring

result = truncate_at_middle_of_filename("C:\\Windows\\SysWOW64\\catroot\\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}",60)
print(f'Truncated: {result}   length {len(result)}')

# Truncated: C:\Windows\...\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}   length 53

【讨论】:

    猜你喜欢
    • 2011-04-12
    • 2019-10-31
    • 1970-01-01
    • 2012-10-15
    • 2020-04-26
    • 1970-01-01
    • 2016-12-06
    • 2011-12-29
    • 2016-06-30
    相关资源
    最近更新 更多