I have a directory I want to save files to, saved as a Path object called dir. I want to autogenerate files names at that path using string concatenation.
The only way I can get this to work in a single line is just through string concatenation:
dir = Path('./Files')
constantString = 'FileName'
changingString = '_001'
path2newfile = dir.as_posix() + '/' + constantString + changingString
print(path2newfile) # ./Files/Filename_001
... which is overly verbose and not platform independent.
What I'd want to do is use pathlib's / operator for easy manipulation of the new file path that is also platform independent. This would require ensuring that the string concatenation happens first, but the only way I know how to do that is to set a (pointless) variable:
filename = constantString + changingString
path2newfile = dir / filename
But I honestly don't see why this should have to take two lines.
If I instead assume use "actual" strings (ie. not variables containing strings), I can do something like this:
path2newfile = dir / 'Filename' '_001'
But this doesn't work with variables.
path2newfile = dir / constantString changingString
# SyntaxError: invalid syntax
So I think the base question is how do I control the order of operators in python? Or at least make the concatenation operator + act before the Path operator /.
Keep in mind this is a MWE. My actual problem is a bit more complicated and has to be repeated several times in the code.
Just use parentheses surrounding your string contatenation:
path2newfile = dir / (constantString + changingString)
Have you considered using Python f-strings?
It seems like your real-world example has a "template-y" feel to it, so something like:
path / f"constant part {variable_part}"
may work.
Use os.path.join().
It's both platform-independent and you can plug the desired path parts as arguments.
Related
I have a path as a string like this:
path = "directory/folder1/folder2/folder3/file1.txt"
I want to know how many levels this path has - in this case 4 (directory, folder1, folder2, folder3).
What's the best way to do it in Python? I thought about counting the "/":
path.count("/")
but I am wondering if there is a better way.
You could do it quite easily using pathlib:
from pathlib import Path
path = Path("directory/folder1/fodler2/folder3/file1.txt")
print(len(path.parents), list(path.parents))
Which gives:
5 [Path('directory/folder1/fodler2/folder3'), Path('directory/folder1/fodler2'), Path('directory/folder1'), Path('directory'), Path('.')]
As can be seen, the results is 5 because "." is also in the list as "directory/folder1/fodler2/folder3/file1.txt" is implicitly equal to "./directory/folder1/fodler2/folder3/file1.txt" so you can always just subtract 1 from the result.
Compared to path.count('/'), this is platform-independent...
It all depends on how precise you want to be. Problems I can think of:
Are you sure the last part of the string is the filename? If it is a directory, does it matter?
Are you sure the path separator is '/'? os.sep is your separator.
What if the string starts with os.sep?
What if some os.sep is repeated? For example os.path.exists("/var//log") returns True.
This might be slightly better, but the solution with pathlib.Path is definitely better.
os.path.normpath(path).count(os.sep)
Maybe one optimal solution could be path.count(os.sep). However, Tomerikoo answer is better than this. However, Make sure that pathlib module is installed, as this module does not come by default in the standard distribution of python2. But if you are using python3. Then it comes by default.
I am new to python, and am trying to understand a script that has the following lines:
dotInd = fileName.find(".")
if dotInd <> -1:
newFC = fileName[0:dotInd]
outFC = newFC + "_buffer"
else:
outFC = fileName + "_buffer"
I have not been able to find what fileName.find(".") is doing, and what the condition dotInd<>-1 means
(Confused about the <> thing)
Any help would be apreciated, also, is there a place where you cand find a list of what all python functions do? Thanks
fileName is an identifier, and refers to an object of type str. You are looking for str.find(). The method returns -1 if the sought-after text is not found, a position otherwise.
<> is an archaic and deprecated way of spelling !=, so it tests if the '.' has been found; if so, the returned position is used to slice the string, removing everything from the '.' to the end.
The code could be better written as:
outFC = fileName.partition('.')[0] + '_buffer'
which will result in the same output without str.find() and testing the output. See the str.partition() function documentation for more information.
It would be more correct still to use os.path.splitext() function to prevent splitting on a leading . (signifying a hidden file on POSIX systems):
import os.path
outFC = os.path.splitext(fileName)[0] + '_buffer'
System: Python 2.6 on Windows 7 64 bit
Recently I did a lot of path formatting in Python. Since the string modifications are always dangerous I started to do it the proper way by using the 'os.path' module.
The first question is if this is the right way to to handle incoming paths? Or can I somehow optimize this?
sCleanPath = sRawPath.replace('\n', '')
sCleanPath = sCleanPath.replace('\\', '/')
sCleanPythonPath = os.path.normpath(sCleanPath)
For formatting the 'sCleanPythonPath' now I use only functions from the 'os.path' module. This works very nice and I didn't had any problems so far.
There is only one exception. I have to change paths so they point not longer on a network storage but on a local drive. Is started to use 'os.path.splitunc()' in conjunction with 'os.path.join()'.
aUncSplit = os.path.splitunc(sCleanPythonUncPath)
sLocalDrive = os.path.normpath('X:/mount')
sCleanPythonLocalPath = (os.path.join(sLocalDrive, aUncSplit[1]))
Unfortunately this doesn't work due to the nature of how absolute paths are handled using 'os.path.join()'. All the solutions I found in the web are using string replacements again which I want to avoid by using the 'os.path' module. Have I overseen anything? Is there another, maybe a better way to do this?
All comments on this are very welcome!
You could optimize the first part slightly by removing the replace() call because on Windows normpath() converts forward slashes to backward slashes:
sCleanPath = sRawPath.replace('\n', '')
sCleanPythonPath = os.path.normpath(sCleanPath)
Here's something that would make the second part of your question work without doing a string replacement:
sSharedFolder = os.path.relpath(os.path.splitunc(sCleanPythonUncPath)[1], os.sep)
sLocalDrive = os.path.normpath('X:/mount') # why not hardcode the result?
sCleanPythonLocalPath = os.path.join(sLocalDrive, sSharedFolder)
The Code:
MOST_POPULAR_REPORTS_LOCATION = '/tmp'
MOST_POPULAR_REPORTS_FILE = 'filename.xml'
TEMP_DATA_FILE_LOCATION = '/tmp/other_location'
subprocess.call(["/bin/cp","-a","MOST_POPULAR_REPORTS_LOCATION MOST_POPULAR_REPORTS_FILE","TEMP_DATA_FILE_LOCATION"])
What do I put between MOST_POPULAR_REPORTS_LOCATION and MOST_POPULAR_REPORTS_FILE to put a / there? I have tried various combinations without success.
What I want is to separate the two variables with a /, so that it reads /tmp/filename.xml by using variable substitution. I do not want to hardcode the paths or filenames as they are used throughout the script.
Use os.path.join:
subprocess.call(["/bin/cp", "-a",
os.path.join(MOST_POPULAR_REPORTS_LOCATION, MOST_POPULAR_REPORTS_FILE),
TEMP_DATA_FILE_LOCATION])
You should not put variable names in quotes, or you'll get string literals. This is not shell.
let's say i have directory paths looking like this:
this/is/the/basedir/path/a/include
this/is/the/basedir/path/b/include
this/is/the/basedir/path/a
this/is/the/basedir/path/b
In Python, how can i split these paths up so they will look like this instead:
a/include
b/include
a
b
If i run os.path.split(path)[1] it will display:
include
include
a
b
What should i be trying out here, should i be looking at some regex command or can this be done without it? Thanks in advance.
EDIT ALL: I solved it using regular expressions, damn handy tool :)
Perhaps something like this, depends on how hardcoded your prefix is:
def removePrefix(path, prefix):
plist = path.split(os.sep)
pflist = prefix.split(os.sep)
rest = plist[len(pflist):]
return os.path.join(*rest)
Usage:
print removePrefix("this/is/the/basedir/path/b/include", "this/is/the/basedir/path")
b/include
Assuming you're on a platform where the directory separator (os.sep) really is the forward slash).
This code tries to handle paths as something a little more high-level than mere strings. It's not optimal though, you could (or should) do more cleaning and canonicalization to be safer.
Maybe something like this:
result = []
prefix = os.path.commonprefix(list_of_paths)
for path in list_of_paths:
result.append(os.path.relpath(path, prefix))
This works only in 2.6. The relapath in 2.5 and before does the work only in case the path is the current working directory.
what about partition?
It Split the string at the first occurrence of sep, and return a 3-tuple containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return a 3-tuple containing the string itself, followed by two empty strings.
data = """this/is/the/basedir/path/a/include
this/is/the/basedir/path/b/include
this/is/the/basedir/path/a
this/is/the/basedir/path/b"""
for line in data.splitlines():
print line.partition("this/is/the/basedir/path/")[2]
#output
a/include
b/include
a
b
Updated for the new comment by author:
It looks like u need rsplit for different directories by whether the directory endswith "include" of not:
import os.path
data = """this/is/the/basedir/path/a/include
this/is/the/basedir/path/b/include
this/is/the/basedir/path/a
this/is/the/basedir/path/b"""
for line in data.splitlines():
if line.endswith('include'):
print '/'.join(line.rsplit("/",2)[-2:])
else:
print os.path.split(line)[1]
#or just
# print line.rsplit("/",1)[-1]
#output
a/include
b/include
a
b
While the criterion is not 100% clear, it seems from the OP's comment that the key issue is specifically whether the path's last component ends in "include". If that is the case, and to avoid going wrong when the last component is e.g. "dontinclude" (as another answer does by trying string matching instead of path matching), I suggest:
def lastpart(apath):
pieces = os.path.split(apath)
final = -1
if pieces[-1] == 'include':
final = -2
return '/'.join(pieces[final:])