在Ruby(不是Rails)中,有没有一种简单的方法来采用长文件路径并用省略号替换其中不重要的部分,以使其更短的显示时间?
例如:
a/非常/长/路径/with/plenty/of/字符/that/won't/fit/on/my/screen/easy
变成类似这样:
a/非常/lo.../screen/easy
我需要能够指定最大长度;路径的起点和终点应该始终可见。
如果我用头撞它足够长的时间,我可能会想出一个解决方案,但也许有人知道一种方法?
我使用了左右指针(lt_ptr
和rt_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#index 和 String#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
有点像这样:
shortener = ->(path, length) {
l = length / 2 - 1
[path[0...l], path[-l..-1]].join('..')
}
shortener.(path, 10)
#⇒ "a/ve..sily"
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