[code] Split a long pathstring ~in half at the nearest '/'
-
[ code ] Split a long pathstring ~in half at the nearest '/'
Scenario:
(a) You have a very long absolute pathstring, and you want to display it in aUI.messagebox, but don't want the dialog to be tremendously wide (or definately not wider than the screen.)
(b) You want the pathstring to look "correct", having the split on the nearest '/' near the middle of the string.Question: The following code is what I came up with (as I could not find any built-in Enumerable iterator that would do what I wanted.) Can anyone suggest a better or more consise (more readable,) way of doing this?
So you would have code that needs the method, like:
<span class="syntaxdefault">info </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"Couldn't load;\n\n"<br /></span><span class="syntaxdefault">info</span><span class="syntaxkeyword"><<</span><span class="syntaxdefault"> path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">pathstring</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">info</span><span class="syntaxkeyword"><<</span><span class="syntaxdefault"> </span><span class="syntaxstring">"\n\nAnswer this question for some action?"<br /></span><span class="syntaxdefault">choice </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> UI</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">messagebox</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> info</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> MB_YESNO</span><span class="syntaxkeyword">|</span><span class="syntaxdefault">96 </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">choice </span><span class="syntaxkeyword">==</span><span class="syntaxdefault"> IDYES </span><span class="syntaxkeyword">?</span><span class="syntaxdefault"> do_sometask</span><span class="syntaxkeyword">()</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">;</span><span class="syntaxdefault"> do_othertask<br /></span>And the
path_knife()method:<span class="syntaxdefault">def path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">60</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> path </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> if path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">></span><span class="syntaxdefault"> max<br /> </span><span class="syntaxcomment"># cut the file path into 2 lines<br /></span><span class="syntaxdefault"> spa </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0.54</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.58</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.62</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.66</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.71</span><span class="syntaxkeyword">]<br /></span><span class="syntaxdefault"> i</span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> for e in spa<br /> c </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">((</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword">)*</span><span class="syntaxdefault">e</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i<br /> x </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> while c </span><span class="syntaxkeyword">>=</span><span class="syntaxdefault"> 0<br /> </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">c</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">]==</span><span class="syntaxstring">'/'</span><span class="syntaxkeyword">)?(</span><span class="syntaxdefault">break c</span><span class="syntaxkeyword">);((</span><span class="syntaxdefault">c</span><span class="syntaxkeyword">==</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">)?(</span><span class="syntaxdefault">break 0</span><span class="syntaxkeyword">);(</span><span class="syntaxdefault">c </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> c</span><span class="syntaxkeyword">-</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">))<br /></span><span class="syntaxdefault"> end </span><span class="syntaxcomment"># while<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">x</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">to_f</span><span class="syntaxkeyword">/(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">to_f</span><span class="syntaxkeyword">-</span><span class="syntaxdefault">1.0</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">>=</span><span class="syntaxdefault"> 0.45</span><span class="syntaxkeyword">)?(</span><span class="syntaxdefault">break x</span><span class="syntaxkeyword">);((</span><span class="syntaxdefault">e</span><span class="syntaxkeyword">==</span><span class="syntaxdefault">spa</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">last</span><span class="syntaxkeyword">)?(</span><span class="syntaxdefault">break 0</span><span class="syntaxkeyword">);(</span><span class="syntaxdefault">next</span><span class="syntaxkeyword">))<br /></span><span class="syntaxdefault"> end </span><span class="syntaxcomment"># for<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment"># <br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">"#{path[0,i+1]} \n#{path[i+1,path.length-1]} "</span><span class="syntaxdefault"> if i</span><span class="syntaxkeyword">></span><span class="syntaxdefault">0<br /> return </span><span class="syntaxstring">"#{path} "</span><span class="syntaxdefault"> </span><span class="syntaxcomment"># just in case i is still 0<br /></span><span class="syntaxdefault"> else </span><span class="syntaxcomment"># leave it as one line<br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">"#{path} "<br /></span><span class="syntaxdefault"> end<br />end </span><span class="syntaxcomment"># def<br /></span><span class="syntaxdefault"> </span>Here's an "unwound" edition of the same method, with annotation comments:
<span class="syntaxdefault">def path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">60</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> path </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> if path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">></span><span class="syntaxdefault"> max<br /> </span><span class="syntaxcomment"># cut the file path into 2 lines<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment"># start point array of %<br /></span><span class="syntaxdefault"> spa </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0.54</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.58</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.62</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.66</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">0.71</span><span class="syntaxkeyword">]<br /></span><span class="syntaxdefault"> i</span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> for e in spa<br /> </span><span class="syntaxcomment"># Get a start index at e% into path.<br /></span><span class="syntaxdefault"> c </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">((</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword">)*</span><span class="syntaxdefault">e</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i<br /> <br /> </span><span class="syntaxcomment"># Iterate backwards from startpoint<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment"># until a '/' character is found, or<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment"># 0 is reached. Return result in x.<br /></span><span class="syntaxdefault"> x </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> while c </span><span class="syntaxkeyword">>=</span><span class="syntaxdefault"> 0<br /> if </span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">c</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">]==</span><span class="syntaxstring">'/'</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> break c<br /> else<br /> if c</span><span class="syntaxkeyword">==</span><span class="syntaxdefault">0<br /> break 0<br /> else<br /> c </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> c</span><span class="syntaxkeyword">-</span><span class="syntaxdefault">1<br /> end<br /> end<br /> end </span><span class="syntaxcomment"># while<br /></span><span class="syntaxdefault"> <br /> </span><span class="syntaxcomment"># Test x to see if it's at least 45% of path;<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment"># if so, return result in i. If not, next.<br /></span><span class="syntaxdefault"> if x</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">to_f</span><span class="syntaxkeyword">/(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">to_f</span><span class="syntaxkeyword">-</span><span class="syntaxdefault">1.0</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">>=</span><span class="syntaxdefault"> 0.45<br /> break x<br /> else<br /> break 0 if e</span><span class="syntaxkeyword">==</span><span class="syntaxdefault">spa</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">last<br /> end<br /> end </span><span class="syntaxcomment"># for<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">"#{path[0,i+1]} \n#{path[i+1,path.length-1]} "</span><span class="syntaxdefault"> if i</span><span class="syntaxkeyword">></span><span class="syntaxdefault">0<br /> return </span><span class="syntaxstring">"#{path} "</span><span class="syntaxdefault"> </span><span class="syntaxcomment"># just in case i is still 0<br /></span><span class="syntaxdefault"> else </span><span class="syntaxcomment"># leave it as one line<br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">"#{path} "<br /></span><span class="syntaxdefault"> end<br />end </span><span class="syntaxcomment"># def<br /></span><span class="syntaxdefault"> </span> -
This is what a dialog using
path_knife()looks like:
-
def path_knife(path,max=60) path = File.expand_path(path) if path.length > max s = [] minlen = path.length path.gsub(/\//) {|piece| len = ($`.length - $'.length).abs if len < minlen then s = [ $`, $'] minlen = len end } return "#{s[0]}/\n#{s[1]}" else # leave it as one line return "#{path}" end end # def puts path_knife("C;/Program Files/Google/Google SketchUp 8/Plugins/test/error/NoMemoryError.rb") -
How about:
def path_knife( path, max=60 ) fpath = File.expand_path(path) if( fpath.length > max ) rMax = fpath[0..max].reverse =~ /\// "#{fpath[0..(max - rMax)]}\n\t#{fpath[(max + 1 - rMax)...fpath.length]}" else "#{fpath}" end end -
Looks good david, but it doesn't satisfy
@unknownuser said:
(b) You want the pathstring to look "correct", having the split on the nearest '/' near the middle of the string.
-
Isn't this a 'vicious' little Ruby exercise ??

At first blush, you think, "Oh simple string manipulation. No problem.." Then you actually try and code it.
It's a good one for a Ruby class, for sure.
@Todd: I had forgotten about the
$`` (prematch) and$'` (postmatch) globals. That could simplify things, especially the string output at the end as you showed.@Dave: I had entertained the idea of
dup'ing and reversing the path, and then using the standardStringclass iterator.each_byteand then stoping when a '/' was found between 45%..55% of the path length. I just didn't want to figure out that night, how to invert the reverse index. -
Well, I am using "max" to determine about where to split the path string. The assumption being that you know what string length looks OK in general. That should be a valid assumption for most legitimate path strings (at least, it would be for any system I've ever encountered). If strings get much longer than that, you'll need a slightly modified version to split into more than 2 lines. The code I provided could easily be modified to take fpath.length/2 to approximate the middle of the path string on the fly. Or, it could easily be modified to do splits into more than 2 lines.
-
Why not ?
<span class="syntaxdefault">def path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> max</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">60</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> return path if path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword"><=</span><span class="syntaxdefault">max<br /> return path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0.</span><span class="syntaxkeyword">.(</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i</span><span class="syntaxkeyword">]+</span><span class="syntaxstring">"..."</span><span class="syntaxkeyword">+</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[-(</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2.</span><span class="syntaxkeyword">.-</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">]<br /></span><span class="syntaxdefault">end</span><span class="syntaxcomment">#def </span><span class="syntaxdefault"></span>
-
@tig said:
Why not ?
<span class="syntaxdefault">def path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,</span><span class="syntaxdefault"> max</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">60</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> return path if path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword"><=</span><span class="syntaxdefault">max<br /> return path</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0.</span><span class="syntaxkeyword">.(</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i</span><span class="syntaxkeyword">]+</span><span class="syntaxstring">"..."</span><span class="syntaxkeyword">+</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">[-(</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i</span><span class="syntaxkeyword">/</span><span class="syntaxdefault">2.</span><span class="syntaxkeyword">.-</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">]<br /></span><span class="syntaxdefault">end</span><span class="syntaxcomment">#def </span><span class="syntaxdefault"></span>
That does not meet the specifications. It will divide folder names.
(The instructor grades you a C- for your effort.)
EDIT: .. AND it does not return a two line string (or add padding to which is lacking in the API implementation of the
UI.messagboxmethod.)EDIT2: Tested your suggestion and for the test path, it returns:
%(#008000)["C:/Program Files/Google/Google ...oMemoryError.rb"]
So, it also chops off part the filename at the end. -
Ok here's my current version:
<span class="syntaxdefault">def self</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">path_knife</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">,</span><span class="syntaxdefault">max</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">60</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment"># make sure it's a Ruby path using '/'.<br /></span><span class="syntaxdefault"> path </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> File</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">expand_path</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> if path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length </span><span class="syntaxkeyword">></span><span class="syntaxdefault"> max<br /> </span><span class="syntaxcomment"># cut the file path into 2 lines<br /></span><span class="syntaxdefault"> ok </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">((</span><span class="syntaxdefault">path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">length</span><span class="syntaxkeyword">-</span><span class="syntaxdefault">1</span><span class="syntaxkeyword">)*</span><span class="syntaxdefault">0.45</span><span class="syntaxkeyword">).</span><span class="syntaxdefault">to_i<br /> cut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> path</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">split</span><span class="syntaxkeyword">(</span><span class="syntaxcomment">//).each_with_index { |e,i|<br /></span><span class="syntaxdefault"> break i if</span><span class="syntaxkeyword">(</span><span class="syntaxdefault"> e</span><span class="syntaxkeyword">==</span><span class="syntaxstring">'/'</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">&&</span><span class="syntaxdefault"> i</span><span class="syntaxkeyword">>=</span><span class="syntaxdefault">ok </span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault"> </span><span class="syntaxkeyword">}<br /></span><span class="syntaxdefault"> cut </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> 0 unless cut</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">is_a</span><span class="syntaxkeyword">?(Integer)<br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">" #{path[0..cut]} \n #{path[(cut+1)..-1]} "</span><span class="syntaxdefault"> if cut</span><span class="syntaxkeyword">></span><span class="syntaxdefault">0<br /> end<br /> </span><span class="syntaxcomment"># otherwise, leave it as one line<br /></span><span class="syntaxdefault"> return </span><span class="syntaxstring">" #{path} "<br /></span><span class="syntaxdefault"> </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">end </span><span class="syntaxcomment"># def<br /></span><span class="syntaxdefault"> </span>**EDIT: Added test after iterator, to set
cutto 0, if a string had no '/' characters (in which caseeach_with_indexjust returns the array characters.)
Also removed frivolous line before iterator:cut = 0EDIT 2:(2011-02-19) Simplified
pathsubstring references (line 11.)
Removed frivolouselseclause (were lines 12 and 13.)**
Advertisement