<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>the corioblog &#187; Perl</title>
	<atom:link href="http://www.coriolinus.net/tag/perl/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.coriolinus.net</link>
	<description>read, and be entertained</description>
	<lastBuildDate>Sat, 09 Jul 2011 19:53:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=</generator>
		<item>
		<title>conf.py: One-upping ConfigParser</title>
		<link>http://www.coriolinus.net/2010/11/13/conf-py-one-upping-configparser/</link>
		<comments>http://www.coriolinus.net/2010/11/13/conf-py-one-upping-configparser/#comments</comments>
		<pubDate>Sat, 13 Nov 2010 14:39:13 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[Class]]></category>
		<category><![CDATA[Computer programming]]></category>
		<category><![CDATA[computing]]></category>
		<category><![CDATA[Creative Commons]]></category>
		<category><![CDATA[Human Interest]]></category>
		<category><![CDATA[Main function]]></category>
		<category><![CDATA[Method]]></category>
		<category><![CDATA[Object-oriented programming]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[Perl module]]></category>
		<category><![CDATA[programmer]]></category>
		<category><![CDATA[Software design patterns]]></category>
		<category><![CDATA[software engineering]]></category>
		<category><![CDATA[wb]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/?p=3135</guid>
		<description><![CDATA[There comes a time in every programmer&#8217;s life when they decide that some silly, common library that they use all the time just isn&#8217;t good enough. It takes too many actions, or it feels opaque, or there are obvious features conspicuous in their absence. For me, that time is now, that library is ConfigParser, and [...]]]></description>
			<content:encoded><![CDATA[<p>There comes a time in every programmer&#8217;s life when they decide that some silly, common library that they use all the time just <em>isn&#8217;t good enough</em>. It takes too many actions, or it feels opaque, or there are obvious features conspicuous in their absence. </p>
<p>For me, that time is now, that library is ConfigParser, and the replacement is included below the fold. It&#8217;s called conf, and it means that the only interaction you as a programmer are required to have with your config file is to assign and/or read values to/from it. Assigning and reading look exactly like normal attribute assignment/reading. </p>
<p>Subversion/TRAC string: <a href="https://trac.coriolinus.net/browser/OOconf">https://svn.coriolinus.net/OOconf</a><br />
distutils packaged version: <a href="https://trac.coriolinus.net/browser/OOconf/dist/conf-0.2.0.zip?format=raw">conf-0.2.0</a></p>
<p>As always, I&#8217;m too cheap to pay a thousand dollars for a site certificate, so please ignore any certificate mismatch errors you encounter when viewing the https side of the site. If you don&#8217;t trust me enough to click through, you&#8217;ll still find the current version under the fold.</p>
<p><span id="more-3135"></span></p>
<pre class="brush: python">
import os
import codecs
import json
from ConfigParser import SafeConfigParser

def internal(func):
	def tf(self, *args, **kwargs):
		self.__dict__[&#039;__conf__&#039;].__dict__[&#039;__internal__&#039;] += 1
		try:
			return func(self, *args, **kwargs)
		finally:
			self.__dict__[&#039;__conf__&#039;].__dict__[&#039;__internal__&#039;] -= 1
	return tf

class Section(object):
	def __init__(self, conf, name, fullname=None):
		if fullname is None:
			fullname = name

		self.__dict__[&#039;__conf__&#039;] = conf
		self.__name__ = name
		self.__fullname__ = fullname

		self.__dict__[&#039;subsections&#039;] = set()
		self.__dict__[&#039;attributes&#039;] = set()

		#this lines MUST be the last in __init__
		self.__restrictedvars__ = set((i for i in dir(self) if &#039;__&#039; not in i))

	@internal
	def __setattr__(self, name, value):
		if self.restricted(name):
			if (not self.__conf__.__restrict__) or self.__conf__.__internal__ &gt; 0:
				self.__dict__[name] = value
			else:
				raise AttributeError(&quot;Namespace conflict: %s restricted for Conf use.&quot; % name)
		else:
			self.__conf__.__new_data__ = True
			self.attributes.add(name)
			self.__conf__.__cp__.set(self.__fullname__, name, json.dumps(value))

	@internal
	def __getattr__(self, name):
		if self.restricted(name):
			return self.__dict__[name]
		else:
			return json.loads(self.__conf__.__cp__.get(self.__fullname__, name))

	@internal
	def __delattr__(self, name):
		if self.restricted(name):
			if (not self.__conf__.__restrict__) or self.__conf__.__internal__ &gt; 0:
				del self.__dict__[name]
			else:
				raise AttributeError(&quot;Namespace conflict: %s restricted for Conf use.&quot; % name)
		else:
			self.__conf__.__new_data__ = True
			self.attributes.remove(name)
			self.__conf__.__cp__.remove_option(self.__name__, name)

	@internal
	def restricted(self, name):
		&quot;&quot;&quot;
		This function returns true for all attributes which should be stored locally, not in the
		configuration file proper.
		&quot;&quot;&quot;
		if name.startswith(&#039;__&#039;) or name.endswith(&#039;__&#039;):
			return True
		if name in self.__restrictedvars__:
			return True

		return False

	@internal
	def add_section(self, name):
		fullname = &#039;&#039;.join((self.__fullname__, &#039;.&#039;, name))

		if hasattr(self, name):
			raise ValueError(&quot;Namespace conflict: %s already in use&quot; % fullname)

		if not self.__conf__.__cp__.has_section(fullname):
			self.__conf__.__cp__.add_section(fullname)
		self.subsections.add(name)
		self.__dict__[name] = Section(self.__conf__, name, fullname)
		self.__restrictedvars__.add(name)
		self.__new_data__ = True

	@internal
	def remove_section(self, name):
		fullname = &#039;&#039;.join((self.__fullname__, &#039;.&#039;, name))

		if not hasattr(self, name):
			raise ValueError(&quot;Can&#039;t remove section %s, as it doesn&#039;t exist&quot; % fullname)

		sub = getattr(self, name)
		for subsub in list(sub.subsections):
			sub.remove_section(subsub)

		self.__conf__.__cp__.remove_section(fullname)
		self.subsections.remove(name)
		self.__restrictedvars__.remove(name)
		del self.__dict__[name]
		self.__new_data__ = True

class Conf(Section):
	&quot;&quot;&quot;
	Automatic storage and retrieval of arbitrary values into a config file. 

	Uses type information and automatic reconversions to store a variety of primitive types in a
	perfectly human-readable format. Primitive types are those encodable by the json module.

	Usage:
	&gt;&gt;&gt; from conf import Conf
	&gt;&gt;&gt; conf = Conf() #or Conf(filename, defaultsectionname)
	&gt;&gt;&gt; conf.foo = &#039;hello world&#039;
	&gt;&gt;&gt; conf.bar = 723
	&gt;&gt;&gt; conf.baz = False
	&gt;&gt;&gt; conf.flush()

	[exit, start a new session here]

	&gt;&gt;&gt; conf = Conf()
	&gt;&gt;&gt; conf.foo
	&#039;hello world&#039;
	&gt;&gt;&gt; conf.bar
	723
	&gt;&gt;&gt; conf.baz
	False

	If you want implicit file creation, you need to use a with statement:
	&gt;&gt;&gt; with Conf() as conf:
	...      conf.foo = 2783.1
	...
	&gt;&gt;&gt; del conf
	&gt;&gt;&gt; with Conf() as otherConf:
	...      otherConf.foo
	...
	2783.1
	&quot;&quot;&quot;
	def __init__(self, filename =&#039;.conf&#039;, sectionName=None):
		&quot;&quot;&quot;
		Initialize a new Conf object.
		&quot;&quot;&quot;
		self.__dict__[&#039;__internal__&#039;] = 0
		self.__dict__[&#039;__restrict__&#039;] = False
		#the above is magic; it must come first

		#sanity check
		if sectionName is not None and &#039;.&#039; in sectionName:
			raise ValueError(&quot;Namespace: &#039;.&#039; cannot be part of a section name&quot;)

		#initialize the superclass
		Section.__init__(self, self, sectionName if sectionName is not None else &#039;config&#039;)

		self.__filename__ = filename
		self.__cp__ = SafeConfigParser()

		#load and initialize the configparser
		if os.path.exists(filename):
			with codecs.open(filename, &#039;rb&#039;, &#039;utf8&#039;) as cf:
				self.__cp__.readfp(cf, filename)

		if sectionName is None:
			topLevelSections = [s for s in self.__cp__.sections() if s.count(&#039;.&#039;) == 0]
			if len(topLevelSections) == 1:
				sectionName = topLevelSections[0]
				self.__name__ = sectionName
				self.__fullname__ = sectionName
			else:
				sectionName = &#039;config&#039;

		#create the default section
		if not self.__cp__.has_section(sectionName):
			self.__cp__.add_section(sectionName)

		#load default section attributes
		self.attributes = set(self.__cp__.options(sectionName))

		#load the various sections
		#first, sort them by the number of dots they contain
		secs = [(s.count(&#039;.&#039;), s) for s in self.__cp__.sections() if s != sectionName]
		secs.sort()
		for name in [sec for count, sec in secs]:
			if &#039;.&#039; not in name:
				self.subsections.add(name)
				self.__dict__[name] = Section(self, name)
				self.__dict__[name].attributes = set(self.__cp__.options(name))
				self.__restrictedvars__.add(name)
			else:
				fullname = name
				rest, name = name.rsplit(&#039;.&#039;, 1)
				sec = self
				for part in rest.split(&#039;.&#039;):
					sec = getattr(sec, part)
				sec.subsections.add(name)
				sec.__dict__[name] = Section(self, name, fullname)
				sec.__dict__[name].attributes = set(self.__cp__.options(fullname))
				sec.__restrictedvars__.add(name)

		self.__new_data__ = False

		#this must come last:
		self.__internal__ = 0
		self.__restrict__ = True
		#This is used in combination with the @internal decorator. Each method so decorated
		# increments this variable on entry, and decrements it on exit. They can then check: is
		# __internal__ &gt; 0? If yes, they were called from within this Conf object, and can adjust
		# their behavior accordingly.
		#
		#Note that we initialize it here to 0. When this __init__ exits, it decrements to -1. This
		# is intentional. Since each internal function starts by incrementing it, this means that
		# only if the variable is &gt; 0 was its caller also internal.

	@internal
	def __enter__(self):
		return self

	@internal
	def __exit__(self, exc_type, exc_value, traceback):
		if self.__new_data__:
			self.flush()

	@internal
	def flush(self):
		with codecs.open(self.__filename__, &#039;wb&#039;, &#039;utf8&#039;) as cf:
			self.__cp__.write(cf)
		self.__new_data__ = False

	@internal
	def add_section(self, name):
		&quot;&quot;&quot;
		Create a new section in the conf file. This will become a dotted extension of the conf object.

		For example:
		&gt;&gt;&gt; c = Conf()
		&gt;&gt;&gt; c.foo = &#039;hello world&#039;
		&gt;&gt;&gt; c.add_section(&#039;bar&#039;)
		&gt;&gt;&gt; c.bar.baz = &#039;world says hello&#039;

		The above turns into a config file which looks like this:
		[config]
		foo = hello world

		[bar]
		baz = world says hello
		&quot;&quot;&quot;
		if hasattr(self, name):
			raise ValueError(&quot;Namespace conflict: %s already in use&quot; % name)
		if &#039;.&#039; in name:
			raise ValueError(&quot;Namespace: &#039;.&#039; cannot be part of a section name&quot;)

		if not self.__conf__.__cp__.has_section(name):
			self.__conf__.__cp__.add_section(name)
		self.subsections.add(name)
		self.__dict__[name] = Section(self, name)
		self.__restrictedvars__.add(name)
		self.__new_data__ = True

	@internal
	def remove_section(self, name):
		&quot;&quot;&quot;
		Remove a section and all included data.
		&quot;&quot;&quot;
		if not hasattr(self, name):
			raise ValueError(&quot;Can&#039;t remove section %s, as it doesn&#039;t exist&quot; % name)

		sub = getattr(self, name)
		for subsub in sub.subsections:
			sub.remove_section(subsub)

		self.__cp__.remove_section(name)
		self.subsections.remove(name)
		self.__restrictedvars__.remove(name)
		del self.__dict__[name]
		self.__new_data__ = True
</pre>
<p><a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /></a><br /><span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">conf.py</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://coriolinus.net" property="cc:attributionName" rel="cc:attributionURL">coriolinus</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License</a>.<br />Permissions beyond the scope of this license may be available at <a xmlns:cc="http://creativecommons.org/ns#" href="http://www.coriolinus.net/contact/" rel="cc:morePermissions">http://www.coriolinus.net/contact/</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2010/11/13/conf-py-one-upping-configparser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>perl is what happens when you irritate a programmer for years</title>
		<link>http://www.coriolinus.net/2006/05/05/perl-is-what-happens-when-you-irritate-a-programmer-for-years/</link>
		<comments>http://www.coriolinus.net/2006/05/05/perl-is-what-happens-when-you-irritate-a-programmer-for-years/#comments</comments>
		<pubDate>Fri, 05 May 2006 20:12:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[opinion]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[Larry Wall]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2006/05/05/874/</guid>
		<description><![CDATA[I hate Perl. Perl is an agglomeration of functionality combining the worst of incomprehensibly terse unix syntax with the worst of every other incomprehensibly terse syntax Larry Wall could get ahold of. There are people who love Perl. Who enjoy producing write-only code. Who are proud that an incredibly high percentage of completely random files [...]]]></description>
			<content:encoded><![CDATA[<p>I hate Perl.</p>
<p>Perl is an agglomeration of functionality combining the worst of incomprehensibly terse unix syntax with the worst of every other incomprehensibly terse syntax Larry Wall could get ahold of.</p>
<p>There are people who love Perl. Who enjoy producing write-only code. Who are <em>proud</em> that an incredibly high percentage of completely random files will be parsed, will do something legal, in Perl. This disgusts me.</p>
<p>I find it hard to look past the fact that in at least five of the last ten or so times an automated build of something I didn&#8217;t write failed, the specific component which failed either was a Perl component or was written in Perl.</p>
<p>I would be almost tempted to look deeper for the source of the problem, maybe to fix it&#8211;if I didn&#8217;t have to deal with Perl.</p>
<p>I sometimes think that Perl was a compromise, or an experiment, to determine exactly how horrible an interpreted language could be and still be widely adopted. Funge was the first experiment. Perl was the second. This astounded the researchers, who had planned a course of twelve, culminating in python. They later released python anyway out of pity.</p>
<p>In a normal language, to access the contents of a variable, you ask for the variable: foo. In Perl, you ask for $@%$foo. The implied profanity cannot be accidental. Actually, scratch the &#8216;implied&#8217;, because you really needed to write $$@%foo and your program doesn&#8217;t work and this error is actually impossible for a human to debug.</p>
<p>This leads to the speculation that Larry Wall is the leader of a race of bugs&#8211;think <em>Men in Black</em>&#8211;who came to Earth to right an injustice which really was only a poor translation. His mission: to do battle with all programmers who want to eliminate bugs from their code. His tactic: release a language in which bugs proliferate like cockroaches in a dump. I&#8217;m not saying this is the truth. But it&#8217;s possible.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2006/05/05/perl-is-what-happens-when-you-irritate-a-programmer-for-years/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>no, what&#8217;s getModel() do?</title>
		<link>http://www.coriolinus.net/2004/02/04/no-whats-getmodel-do/</link>
		<comments>http://www.coriolinus.net/2004/02/04/no-whats-getmodel-do/#comments</comments>
		<pubDate>Wed, 04 Feb 2004 07:06:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[GUI]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2004/02/04/457/</guid>
		<description><![CDATA[Java isn&#8217;t a bad language, really. It&#8217;s got a lot of positive things going for it, and when things work right, they come together quite easily. It&#8217;s just that it&#8217;s so incredibly picky. Say that, for some reason, you want to include a GUI checkbox in a program. You can drop one in with no [...]]]></description>
			<content:encoded><![CDATA[<p>Java isn&#8217;t a bad language, really. It&#8217;s got a lot of positive things going for it, and when things work right, they come together quite easily.</p>
<p>It&#8217;s just that it&#8217;s so incredibly <em>picky</em>.</p>
<p>Say that, for some reason, you want to include a GUI checkbox in a program. You can drop one in with no problems at all; it looks like a checkbox, it can be checked and unchecked, it has a string attached, all seems good. Then, say you want to find out what is the state of the checkbox. It turns out to be LITERALLY IMPOSSIBLE. You are REQUIRED to use Java&#8217;s event-driven methodology, which means more work, and more coding time. The checkbox MUST remember its own state internally; it just refuses to tell you. It&#8217;ll fire off an event, in case you&#8217;ve attached a listener, but that really seems like a whole lot of work to just retrieve a boolean value from a component.</p>
<p>I&#8217;m going to go to sleep, and work on this more tomorrow. I wish there was a language that combined the best features of Java and Perl. That is, I want it to be universally supported, have quick and easy GUI applications&#8230; and not enforce a specific programming style on you. Because maybe if you&#8217;re handling 40 checkboxes in a single page, an event driven model really is easier to code and reduces bugs and all that. But when you&#8217;ve got a single checkbox, in its own container class with some other things, and you just want the state so you can write the toString() for the container class&#8230; it&#8217;s just annoying.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2004/02/04/no-whats-getmodel-do/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>375</title>
		<link>http://www.coriolinus.net/2003/10/09/375/</link>
		<comments>http://www.coriolinus.net/2003/10/09/375/#comments</comments>
		<pubDate>Thu, 09 Oct 2003 18:24:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2003/10/09/375/</guid>
		<description><![CDATA[I keep thinking of programming projects that would be really cool, that I want to work on, that I really shouldn&#8217;t because I have other work that I&#8217;m supposed to be doing. I think it&#8217;s because I&#8217;ve gone too long without taking a course in which I actually program anything. This is funny, because I [...]]]></description>
			<content:encoded><![CDATA[<p>I keep thinking of programming projects that would be really cool, that I want to work on, that I really shouldn&#8217;t because I have other work that I&#8217;m supposed to be doing. I think it&#8217;s because I&#8217;ve gone too long without taking a course in which I actually program anything. This is funny, because I keep taking courses that have promising-sounding titles and imply that good assignments will be had. Instead, they give me essays.</p>
<p>This leads to an odd procrastination problem whereby I do cool things on the computer that make me happy, and to some extent are actual accomplishments, instead of doing the boring work which is typically easier, and a requirement for one course or another. It&#8217;s not that I&#8217;m not being productive, it&#8217;s just that what I&#8217;m producing isn&#8217;t what the courses are asking for.</p>
<p>I wish I could trade in, perhaps, the Perl music-fixing program and a working webcomics directory instead of my ethics of computation essay.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2003/10/09/375/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>you don&#8217;t get to hear the long story</title>
		<link>http://www.coriolinus.net/2003/10/03/you-dont-get-to-hear-the-long-stor/</link>
		<comments>http://www.coriolinus.net/2003/10/03/you-dont-get-to-hear-the-long-stor/#comments</comments>
		<pubDate>Fri, 03 Oct 2003 11:23:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[geekspeak]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2003/10/03/370/</guid>
		<description><![CDATA[Well, that was interesting. The short story is that my computer died, and that I&#8217;m using a public machine right now (and for the conceivable future) because I can no longer log into my own. I guess that the Perl will have to wait a little bit longer.]]></description>
			<content:encoded><![CDATA[<p>Well, that was interesting.</p>
<p>The short story is that my computer died, and that I&#8217;m using a public machine right now (and for the conceivable future) because I can no longer log into my own.</p>
<p>I guess that the Perl will have to wait a little bit longer.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2003/10/03/you-dont-get-to-hear-the-long-stor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Me: How hard can Perl really be? Perl: Ooh, a challenge!</title>
		<link>http://www.coriolinus.net/2003/10/02/me-how-hard-can-perl-really-be-perl-ooh-a-challenge/</link>
		<comments>http://www.coriolinus.net/2003/10/02/me-how-hard-can-perl-really-be-perl-ooh-a-challenge/#comments</comments>
		<pubDate>Thu, 02 Oct 2003 19:20:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2003/10/02/368/</guid>
		<description><![CDATA[Ok, the test program I wrote now works beautifully, as well as providing a bit of an introduction for me into Perl regular expressions. Also, I got down to business and wrote the program specification, detailing in English exactly what the program will do. At this point, all that&#8217;s left is to do is a [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, the test program I wrote now works beautifully, as well as providing a bit of an introduction for me into Perl regular expressions. Also, I got down to business and wrote the program specification, detailing in English exactly what the program will do. At this point, all that&#8217;s left is to do is a little bit of research into how Perl handles functions, and coding the thing up. Shouldn&#8217;t take more than a few hours, total. I am confident that the program will be done by Friday night.</p>
<p>You know, I&#8217;m not sure I ever got around to mentioning what program I&#8217;m writing, or why I&#8217;m using Perl to write it. I&#8217;m going to quote the Program Spec:</p>
<blockquote><p>The purpose of MusicFix is simple: to clean your music directory of duplicate files, and fix oddly formatted filenames. To use it, simply specify the root of a music directory.</p>
<p>MusicFix will recursively traverse that directory. It will then target and fix several odd filename formats&#8230; [cut for length] All filenames will then be normalized&#8230; [cut for length] While doing this renaming, some files may run into duplicates as their names become standardized. In this case, MusicFix will delete the smaller of the two files.</p></blockquote>
<p>It does some other relatively minor stuff, but nothing too exciting. The thing to keep in mind is that this is a dangerous program: its main purpose is to rename and delete files, based on its own ideas of what is RIGHT. I&#8217;m going to test it out pretty thoroughly before letting it anywhere near my actual music. Once it&#8217;s ready, though, I&#8217;m going to make it available for public consumption, because I get the impression that I&#8217;m not the only person who&#8217;s sick of having music on my system which was apparantly ripped by a chimpanzee, to look at the filename.</p>
<p>Oh, and the reason I&#8217;m learning an entirely new language for this project is twofold. One, I haven&#8217;t learned Perl yet, but it&#8217;s been on my list of languages to get a feel of for quite some time now. I figure that writing a moderately complex utility in a language qualifies you to say you&#8217;ve got a feel for it. The other reason is that Perl was designed especially to handle weird regular expressions, which are the simplest way to handle the problem of extracting the information we want from the filenames and rewriting them to make sense.</p>
<p>On a related note, I just borrowed a manual from a friend of mine. On the back, it says &#8220;Teach yourself all about programming with Perl5 in a week!&#8221; I wonder if the authors realized that the average buyer of this book would be either a computer science student or professional programmer, and would therefore be able to pick up most of the applicable concepts with an expended effort of only a few hours? How many people who don&#8217;t otherwise know anything about programming would even believe such a claim?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2003/10/02/me-how-hard-can-perl-really-be-perl-ooh-a-challenge/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>this is nonsense</title>
		<link>http://www.coriolinus.net/2003/10/01/this-is-nonsense/</link>
		<comments>http://www.coriolinus.net/2003/10/01/this-is-nonsense/#comments</comments>
		<pubDate>Thu, 02 Oct 2003 03:52:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2003/10/01/this-is-nonsense/</guid>
		<description><![CDATA[Ok, I have two files: dirread.pl and simpledirtest.pl. contents are as follows: ### dirread.pl # this is a test program # it tries to read the names of files in a directory to a file print &#8220;Please enter the name of the directory you want to list.\n&#8221;; print &#8220;Be careful of capitalization.\n&#8221;; print &#8220;: &#8220;; [...]]]></description>
			<content:encoded><![CDATA[<p>Ok, I have two files: dirread.pl and simpledirtest.pl. contents are as follows:</p>
<p><code>### dirread.pl<br />
# this is a test program<br />
# it tries to read the names of files in a directory to a file</code></p>
<p>print &#8220;Please enter the name of the directory you want to list.\n&#8221;;<br />
print &#8220;Be careful of capitalization.\n&#8221;;<br />
print &#8220;: &#8220;;</p>
<p>$dirname = &lt;STDIN&gt;;</p>
<p># ok, i&#8217;ve got the name of the directory to read; now how about opening the output stream?</p>
<p>open(OUT, &#8216;&gt; filelist.txt&#8217;) or die &#8220;can&#8217;t open file for output! $!&#8221;;</p>
<p># what are the chances that eliminating the error handling code eliminates the error?<br />
# nope, that just eliminated the error message. putting back the code.<br />
opendir(DIR, $dirname) or die &#8220;can&#8217;t opendir $dirname: $!&#8221;;<br />
while (defined($file = readdir(DIR))) {<br />
# do something with &#8220;$dirname/$file&#8221;<br />
print OUT $file . &#8220;\n&#8221;;<br />
}<br />
closedir(DIR);</p>
<p>### simpledirtest.pl<br />
opendir(DIR, &#8220;.&#8221;);     # . is the current directory</p>
<p>while ( $filename = readdir(DIR) ) {<br />
print $filename , &#8220;\n&#8221;;<br />
}</p>
<p>closedir DIR;</p>
<p>Then, I have output as follows:</p>
<p>C:\My Documents\programs\perl\musicfix\test&gt;perl -w simpledirtest.pl<br />
.<br />
..<br />
a.txt<br />
renamer.pl<br />
test.txt<br />
testout.pl<br />
filelist.txt<br />
simpledirtest.pl<br />
dirread.pl</p>
<p>C:\My Documents\programs\perl\musicfix\test&gt;perl -w dirread.pl<br />
Please enter the name of the directory you want to list.<br />
Be careful of capitalization.<br />
: .<br />
can&#8217;t opendir .<br />
: No such file or directory at dirread.pl line 16,  line 1.</p>
<p>C:\My Documents\programs\perl\musicfix\test&gt;</p>
<p>Somebody, anybody, <em>please</em> tell me why this doesn&#8217;t work. I&#8217;m tearing my hair out here.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2003/10/01/this-is-nonsense/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>365</title>
		<link>http://www.coriolinus.net/2003/10/01/365/</link>
		<comments>http://www.coriolinus.net/2003/10/01/365/#comments</comments>
		<pubDate>Wed, 01 Oct 2003 06:59:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2003/10/01/365/</guid>
		<description><![CDATA[When you get down to it, Perl is Qbasic with a facelift and liposuction, on speed. This is a good thing. I intend to learn it enough to write a functional program to fix my .mp3 filetags by tomorrow, having no previous knowledge of the language.]]></description>
			<content:encoded><![CDATA[<p>When you get down to it, Perl is Qbasic with a facelift and liposuction, on speed.</p>
<p>This is a good thing. I intend to learn it enough to write a functional program to fix my .mp3 filetags by tomorrow, having no previous knowledge of the language.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2003/10/01/365/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>i no longer remember the context of this post</title>
		<link>http://www.coriolinus.net/2002/12/21/i-no-longer-remember-the-context-of-this-post/</link>
		<comments>http://www.coriolinus.net/2002/12/21/i-no-longer-remember-the-context-of-this-post/#comments</comments>
		<pubDate>Sat, 21 Dec 2002 16:53:00 +0000</pubDate>
		<dc:creator>coriolinus</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.coriolinus.net/2002/12/21/218/</guid>
		<description><![CDATA[New thought: I may work entirely in java for the main frame, so that I keep the advantages of multithreading without having to deal with multiple-language programming. I&#8217;m going to have to look into this. Is Perl multithreaded? Because if I&#8217;m going to learn a (basically) new language for this project, it might as well [...]]]></description>
			<content:encoded><![CDATA[<p>New thought: I may work entirely in java for the main frame, so that I keep the advantages of multithreading without having to deal with multiple-language programming. I&#8217;m going to have to look into this. Is Perl multithreaded? Because if I&#8217;m going to learn a (basically) new language for this project, it might as well be a multithreaded one&#8230;</p>
<p>The fun part about working with frames is that I can keep the old php code in all the other frames where it&#8217;s still the best-suited language.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.coriolinus.net/2002/12/21/i-no-longer-remember-the-context-of-this-post/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

