diff --git a/aegisub/libaegisub/include/libaegisub/util.h b/aegisub/libaegisub/include/libaegisub/util.h
index 6cbf47e3136ca7d2655f7ad8d8c6d7058f0fc630..d59020d321e0ba0715e683b5f3f36ef89c056a1c 100644
--- a/aegisub/libaegisub/include/libaegisub/util.h
+++ b/aegisub/libaegisub/include/libaegisub/util.h
@@ -31,36 +31,41 @@ namespace agi {
 		TypeDir			///< Directory
 	};
 
-	// Calculate midpoint from a list of Integers
+	/// Clamp `b` to the range [`a`,`c`]
 	template<typename T> inline T mid(T a, T b, T c) { return std::max(a, std::min(b, c)); }
 
-	// Get the parent directory of a path.
-	// @param path Path to process.
+	/// Get the parent directory of a path.
+	/// @param path Path to process.
 	const std::string DirName(const std::string& path);
 
-	// Rename a file.
-	// @param from Source.
-	// @param to   Destination.
+	/// Rename a file.
+	/// @param from Source.
+	/// @param to   Destination.
 	void Rename(const std::string& from, const std::string& to);
 
-	// Get time sutable for logging mechinisms.
-	// @param tv timeval
+	/// Delete a file
+	/// @param path Path to file to delete
+	/// @throws agi::FileNotAccessibleError if file exists but could not be deleted
+	void Remove(std::string const& path);
+
+	/// Get time suitable for logging mechanisms.
+	/// @param tv timeval
 	void time_log(agi_timeval &tv);
 
-	// Make all alphabetic characters lowercase.
-	// @param str Input string
+	/// Make all alphabetic characters lowercase.
+	/// @param str Input string
 	void str_lower(std::string &str);
 
-	// Convert a string to Integer.
-	// @param str Input string
+	/// Convert a string to Integer.
+	/// @param str Input string
 	int strtoi(std::string const& str);
 
 	bool try_parse(std::string const& str, double *out);
 	bool try_parse(std::string const& str, int *out);
 
 	/// Check for amount of free space on a Path.
-	// @param path[in] Path to check
-	// @param type     PathType (default is TypeDir)
+	/// @param path[in] Path to check
+	/// @param type     PathType (default is TypeDir)
 	uint64_t freespace(std::string const& path, PathType type=TypeDir);
 
 	struct delete_ptr {
diff --git a/aegisub/libaegisub/unix/util.cpp b/aegisub/libaegisub/unix/util.cpp
index 230561627565b4121ad5bfc5e97835973b0dd66e..ea556ec6e1ffdc7d638c21c8c3504c7f89c9a98f 100644
--- a/aegisub/libaegisub/unix/util.cpp
+++ b/aegisub/libaegisub/unix/util.cpp
@@ -60,6 +60,11 @@ void Rename(const std::string& from, const std::string& to) {
 	rename(from.c_str(), to.c_str());
 }
 
+void Remove(std::string const& path) {
+	if (!remove(path.c_str()) && errno != ENOENT)
+		throw agi::FileNotAccessibleError("Can not remove file: " + path);
+}
+
 void time_log(timeval &tv) {
 	gettimeofday(&tv, (struct timezone *)NULL);
 }
diff --git a/aegisub/libaegisub/windows/util_win.cpp b/aegisub/libaegisub/windows/util_win.cpp
index 2f55b2e537b4844e7dd6952fb45dea971557c453..995089546e00bd6d4ea25271051f8f58a2999021 100644
--- a/aegisub/libaegisub/windows/util_win.cpp
+++ b/aegisub/libaegisub/windows/util_win.cpp
@@ -57,6 +57,14 @@ void Rename(const std::string& from, const std::string& to) {
 		throw agi::FileNotAccessibleError("Can not overwrite file: " + ErrorString(GetLastError()));
 }
 
+void Remove(std::string const& path) {
+	if (!DeleteFile(ConvertW(path).c_str())) {
+		DWORD err = GetLastError();
+		if (err != ERROR_FILE_NOT_FOUND)
+			throw agi::FileNotAccessibleError("Can not remove file: " + ErrorString(err));
+	}
+}
+
 std::string ErrorString(DWORD error) {
 	LPWSTR lpstr = nullptr;
 
diff --git a/aegisub/tests/libaegisub_util.cpp b/aegisub/tests/libaegisub_util.cpp
index 0dad93dbb75a567f2ecf226a8b517aa739e32d4c..0dcb08c5e732ae8b4d6058809620bcfc8b8f79dd 100644
--- a/aegisub/tests/libaegisub_util.cpp
+++ b/aegisub/tests/libaegisub_util.cpp
@@ -62,6 +62,21 @@ TEST(lagi_util, UtilRenameExNotFound) {
 	EXPECT_THROW(util::Rename("./data/nonexistent", ""), FileNotFoundError);
 }
 
+TEST(lagi_util, RemoveExisting) {
+	std::ofstream("./data/file_to_remove");
+	EXPECT_NO_THROW(util::Remove("./data/file_to_remove"));
+	std::ifstream check("./data/file_to_remove");
+	EXPECT_FALSE(check.good());
+}
+
+TEST(lagi_util, RemoveNonExisting) {
+	EXPECT_NO_THROW(util::Remove("./data/nonexistent"));
+}
+
+TEST(lagi_util, RemoveReadOnly) {
+	EXPECT_THROW(util::Remove("./data/file_read_only"), FileNotAccessibleError);
+}
+
 TEST(lagi_util, Utilstr_lower) {
 	std::string str("-!ABCDEFGHIJKLMNOPQRSTUVWXYZ123");
 	util::str_lower(str);