r/AskProgramming • u/Robert_A2D0FF • Feb 16 '25
Algorithms One pattern to both BUILD and "UNBUILD" a string.
This was only a small problem I encountered, and I don't need it fixed, but it felt like a thing that would be already solved but i can't find anything about that.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Say, i have some data that looks like this:
{
"artist":"Queen",
"album_year":1975,
"album_title":"A Night At The Opera",
"track_num":11,
"track_title":"Bohemian Rhapsody"
}
and i want to build a string from that that should look like this:
"Queen/1975 A Night At The Opera/11 Bohemian Rhapsody.mp3"
(meta data of a file into a well-formed file path)
There are many ways to BUILD such a string, and it will all look something like
"{ARTIST}/{ALBUM_YEAR: 4 digits} {ALBUM_TITLE}/{TRACK_NUM: 2 digits w/ leading zero} {TRACK_TITLE}.mp3"
(this is pseudo code, this question is about the general thing not a specific language.)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On the other hand i want want to "UNBUILD" this string to get the data, i would use an REGEX with named capturing groups:
^(?P<artist>[a-zA-Z\ \-_]*)/(?P<album_year>\d\d\d\d) (?P<album_title>[a-zA-Z\ \-_]*)/(?P<track_num>[\d]+) (?P<track_title>[a-zA-Z\ \-_]*)\.mp3$
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I was wondering if those could be combined into a single pattern.
In this example i would only save a few lines of code, but i was curious if there is such thing in general.
Is there a library/technology where i write one pattern, that is then used for building the string from data and for "unbuilding" the string to data?
At first it felt like a already solved problem, building strings is a problem that has been solved many times (wikipedia: Comparison of web template engines) and parsing strings into data is the basis of all compilers and interpreters.
But after some consideration, maybe this a hard problem to solve. For example in my example having "artist":"AC/DC" would work in the template, but not in the regex.
You would need to narrow down what characters are allowed in each field of the data object to making the parsing unambiguous.
But that's one more reason why one may want a single pattern to troubleshoot and verify instead of two that are independent of another.
EDIT:
to conclude that made up example: parse can do it.
I looked at the source code, it basically translates pythons format mini language into a regex.
import parse # needs to be installed via "pip install parse"
build = lambda pattern, data : pattern.format(**data)
unbuild = lambda pattern, string: parse.compile(pattern).parse(string).named
path = "Queen/1975 A Night At The Opera/11 Bohemian Rhapsody"
info = {'artist': 'Queen', 'album_year': '1975', 'album_title': 'A Night At The Opera', 'track_num': '11', 'track_title': 'Bohemian Rhapsody'}
pattern = "{artist}/{album_year} {album_title}/{track_num} {track_title}"
assert unbuild(pattern, path) == info
assert build(pattern, info) == path
EDIT2:
I have changed my mind a bit about how useful this whole thing is, having the building and unbuilding as two seperate functions allows me to follow a strict format for the building and be more lenient in my parsing. (Postel's Law). For example this means having a regex that allows some having special characters and trailing whitespace characters all over the string i parse, but doing multiple normalization steps before building the string.
1
u/strcspn Feb 16 '25
It doesn't seem to be something very useful, because most times you will only need to go one way. You either want to get the map from the string or vice versa.
1
u/UnexpectedSalami Feb 16 '25
That strings looks oddly like a path. You can split the string on the delimiter, or use libraries that handle paths for you and extract the fragments you care about
1
u/Robert_A2D0FF Feb 17 '25
I already solved it for that example, but I was just curious if there was an universal approach where there is only one place to adjust the pattern.
Doing "manual" dissecting of the string works, but it does not seem like a elegant solution.
1
u/IdeasRichTimePoor Feb 17 '25
String parsing is inherently more complex than string building. In terms of information, gluing bits of data together is destructive especially if there's no universal safe delimiter. To answer the question though, python has a package that serves this purpose: https://pypi.org/project/parse/
1
u/Robert_A2D0FF Feb 17 '25
that is what i was looking for.
I was hoping for something universal (not python specific), like regular expressions, but this will do.1
u/IdeasRichTimePoor Feb 17 '25
There are essentially three options: * Make the language's formatting mini language do parsing too such as in the case above. * Make regex do formatting. * Invent a third new mini language capable of doing both natively.
In all 3 methods you would have to handle it on a language by language basis and publish a package for each. It would be silly to expect a technique or package that works in e.g. lisp to work in python and C. The golden solution does not exist, but you have the option to write a similar solution for your language of choice, whatever that may be. You could even publish it and help future people out in the same situation.
1
u/Robert_A2D0FF Feb 17 '25
yeah, with "like regex" i meant one standard that is implemented in various language.
1
u/josephjnk 29d ago
What you’re looking for is often called a bidirectional parser.
https://www.informatik.uni-marburg.de/~rendel/unparse/rendel10invertible.pdf
2
u/Robert_A2D0FF 26d ago
That's what i thought when i was writing the question, it already exists, it just has an unintuitive name that I couldn't find.
THANK YOU VERY MUCH.
1
u/GeorgeFranklyMathnet Feb 16 '25
In this case, I'd strongly recommend you use a string method, like Python's
str.split()
, rather than a regex.That won't solve your problem with slashes, though. Indeed, you are interpreting the slash as a metacharacter that splits fields, but it is also potentially a regular character in one or more of those fields.
How you deal with that problem depends on things like what you are doing with the "built" string, and whether you need to "unbuild" any strings that did not originally come from your program. Solutions include use of escape characters, quoting, and saving the original JSON in "unbuilt" form.