Function to Concatenate Paths

Function to concatenate paths?

Yes, file.path()

R> file.path("usr", "local", "lib")
[1] "usr/local/lib"
R>

There is also the equally useful system.path() for files in a package:

R> system.file("extdata", "date_time_zonespec.csv", package="RcppBDT")
[1] "/usr/local/lib/R/site-library/RcppBDT/extdata/date_time_zonespec.csv"
R>

which will get the file extdata/date_time_zonespec.csv irrespective of

  1. where the package is installed, and
  2. the OS

which is very handy. Lastly, there is also

R> .Platform$file.sep
[1] "/"
R>

if you insist on doing it manually.

Concatenate path and file

You're using the wrong syntax for calling functions. In Powershell you separate arguments by a space and you also don't need to use ().

, is the array-operator, so "C:\_Store", "run.sql" actually passes an array of two strings to $Path and because you're using the variable (containing an array) in a string, the strings are joined together with a whitespace. $File is empty, which is why it ends with nothing after \.

Try:

fnConcatenatePathFile "C:\_Store" "run.sql"

or to make it clearer:

fnConcatenatePathFile -Path "C:\_Store" -File "run.sql"

Concatenate path and filename

Backslash character (\) has to be escaped in string literals.

  • This is wrong: '\'
  • This is correct: '\\' - this is a string containing one backslash

Therefore, this is wrong:

'C:\Program Files (x86)\LibreOffice 5\program\soffice.exe'

There is a trick!

String literals prefixed by r are meant for easier writing of regular expressions. One of their features is that backslash characters do not have to be escaped. So, this would be OK:

r'C:\Program Files (x86)\LibreOffice 5\program\soffice.exe'

However, that wont work for a string ending in backslash:

  • r'\' - this is a syntax error

So, this is also wrong:

r'C:\Users\A\Desktop\Repo\'

So, I would do the following:

import os
import subprocess

soffice = 'C:\\Program Files (x86)\\LibreOffice 5\\program\\soffice.exe'
outdir = 'C:\\Users\\A\\Desktop\\Repo\\'
full_path = os.path.join(outdir, filename)

subprocess.call([soffice,
'--headless',
'--convert-to', 'pdf',
'--outdir', outdir,
full_path])

Platform independent path concatenation using / , \ ?

You want to use os.path.join() for this.

The strength of using this rather than string concatenation etc is that it is aware of the various OS specific issues, such as path separators. Examples:

import os

Under Windows 7:

base_dir = r'c:\bla\bing'
filename = r'data.txt'

os.path.join(base_dir, filename)
'c:\\bla\\bing\\data.txt'

Under Linux:

base_dir = '/bla/bing'
filename = 'data.txt'

os.path.join(base_dir, filename)
'/bla/bing/data.txt'

The os module contains many useful methods for directory, path manipulation and finding out OS specific information, such as the separator used in paths via os.sep

PHP concatenation of paths

I ran into this problem myself, primarily regarding the normalization of paths.

Normalization is:

  • One separator (I've chosen to support, but never return a backwards slash \\)
  • Resolving indirection: /../
  • Removing duplicate separators: /home/www/uploads//file.ext
  • Always remove trailing separator.

I've written a function that achieves this. I don't have access to that code right now, but it's also not that hard to write it yourself.

Whether a path is absolute or not doesn't really matter for the implementation of this normalization function, just watch out for the leading separator and you're good.

I'm not too worried about OS dependence. Both Windows and Linux PHP understand / so for the sake of simplicity I'm just always using that - but I guess it doesn't really matter what separator you use.


To answer your question: path concatenation can be very easy if you just always use / and assume that a directory has no trailing separator. 'no trailing separator' seems like a good assumption because functions like dirname remove the trailing separator.

Then it's always safe to do: $dir . "/" . $file.

And even if the result path is /home/uploads/../uploads//my_uploads/myfile.ext it's still going to work fine.

Normalization becomes useful when you need to store the path somewhere. And because you have this normalization function you can make these assumptions.


An additional useful function is a function to make relative paths.

  • /files/uploads
  • /files/uploads/my_uploads/myfile.ext

It can be useful to derive from those two paths, what the relative path to the file is.


realpath

I've found realpath to be extremely performance heavy. It's not so bad if you're calling it once but if you're doing it in a loop somewhere you get a pretty big hit. Keep in mind that each realpath call is a call to the filesystem as well. Also, it will simply return false if you pass in something silly, I'd rather have it throw an Exception.

To me the realpath function is a good example of a BAD function because it does two things: 1. It normalizes the path and 2. it checks if the path exists. Both of these functions are useful of course but they must be separated. It also doesn't distinguish between files and directories. For windows this typically isn't a problem, but for Linux it can be.

And I think there is some quirky-ness when using realpath("") on Windows. I think it will return \\ - which can be profoundly unacceptable.


/**
* This function is a proper replacement for realpath
* It will _only_ normalize the path and resolve indirections (.. and .)
* Normalization includes:
* - directiory separator is always /
* - there is never a trailing directory separator
* @param $path
* @return String
*/
function normalize_path($path) {
$parts = preg_split(":[\\\/]:", $path); // split on known directory separators

// resolve relative paths
for ($i = 0; $i < count($parts); $i +=1) {
if ($parts[$i] === "..") { // resolve ..
if ($i === 0) {
throw new Exception("Cannot resolve path, path seems invalid: `" . $path . "`");
}
unset($parts[$i - 1]);
unset($parts[$i]);
$parts = array_values($parts);
$i -= 2;
} else if ($parts[$i] === ".") { // resolve .
unset($parts[$i]);
$parts = array_values($parts);
$i -= 1;
}
if ($i > 0 && $parts[$i] === "") { // remove empty parts
unset($parts[$i]);
$parts = array_values($parts);
}
}
return implode("/", $parts);
}

/**
* Removes base path from longer path. The resulting path will never contain a leading directory separator
* Base path must occur in longer path
* Paths will be normalized
* @throws Exception
* @param $base_path
* @param $longer_path
* @return string normalized relative path
*/
function make_relative_path($base_path, $longer_path) {
$base_path = normalize_path($base_path);
$longer_path = normalize_path($longer_path);
if (0 !== strpos($longer_path, $base_path)) {
throw new Exception("Can not make relative path, base path does not occur at 0 in longer path: `" . $base_path . "`, `" . $longer_path . "`");
}
return substr($longer_path, strlen($base_path) + 1);
}

What is a macro for concatenating an arbitrary number of components to build a path in Rust?

Once you read past the macro syntax, it's not too bad. Basically, we take require at least two arguments, and the first one needs to be convertible to a PathBuf via Into. Each subsequent argument is pushed on the end, which accepts anything that can be turned into a reference to a Path.

macro_rules! build_from_paths {
($base:expr, $($segment:expr),+) => {{
let mut base: ::std::path::PathBuf = $base.into();
$(
base.push($segment);
)*
base
}}
}

fn main() {
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};

let a = build_from_paths!("a", "b", "c");
println!("{:?}", a);

let b = build_from_paths!(PathBuf::from("z"), OsStr::new("x"), Path::new("y"));
println!("{:?}", b);
}


Related Topics



Leave a reply



Submit