跨平台開發中,硬編碼 \n、/、; 是移植性 Bug 溫床。Java 提供 System.getProperty()、FileSystems、LineSeparator 等標準 API 獲取當前平台分隔符。本文整理所有相關屬性、最佳實踐與常見誤區。
| 屬性鍵 | 常量/方法 | Windows | Linux/macOS | 用途 |
|---|---|---|---|---|
line.separator | System.lineSeparator() (Java 7+) | \r\n (0x0D 0x0A) | \n (0x0A) | 文本換行 |
file.separator | File.separator, FileSystems.getDefault().getSeparator() | \ | / | 文件路徑分隔 |
path.separator | File.pathSeparator | ; | : | CLASSPATH/PATH 列表分隔 |
user.dir | System.getProperty("user.dir") | 當前工作目錄 | 當前工作目錄 | 相對路徑基準 |
java.io.tmpdir | System.getProperty("java.io.tmpdir") | C:\Users\xxx\AppData\Local\Temp\ | /tmp/ | 臨時文件目錄 |
import java.io.File;
import java.nio.file.FileSystems;
public class SeparatorUtils {
/** 系統行分隔符 - 最常用 */
public static String lineSeparator() {
return System.lineSeparator(); // 不拋異常、無需緩存
}
/** 文件路徑分隔符 */
public static String fileSeparator() {
return File.separator; // 或 FileSystems.getDefault().getSeparator()
}
/** 路徑列表分隔符 (CLASSPATH 等) */
public static String pathSeparator() {
return File.pathSeparator;
}
/** 組合跨平台路徑 */
public static String joinPath(String... parts) {
return String.join(File.separator, parts);
}
/** 分割路徑為片段 */
public static String[] splitPath(String path) {
return path.split(Pattern.quote(File.separator));
}
public static void main(String[] args) {
System.out.println("line.separator = [" + lineSeparator().replace("\r", "\\r").replace("\n", "\\n") + "]");
System.out.println("file.separator = " + fileSeparator());
System.out.println("path.separator = " + pathSeparator());
System.out.println("joinPath = " + joinPath("home", "user", "docs"));
}
}
public static void printAllProperties() {
Properties props = System.getProperties();
List keys = new ArrayList<>(props.stringPropertyNames());
keys.sort(String::compareTo);
for (String k : keys) {
System.out.printf("%-40s = %s%n", k, props.getProperty(k));
}
}
關鍵屬性分類:
java.version, java.vendor, java.home, java.class.path, java.library.path, java.io.tmpdiros.name, os.arch, os.versionuser.name, user.home, user.dir, user.timezone, user.language, user.countryfile.separator, path.separator, line.separatorPath / Paths.get() (Java 7+):自動處理分隔符、規範化 ..、.。
Path p = Paths.get("config", "app.properties"); // 自動用 file.separator
Files.readString(p); // 直接讀取
String cp = String.join(File.pathSeparator, jars);writer.write(System.lineSeparator()) 或 println() (PrintWriter 自動處理)。Path.of(input).normalize() 防止目錄遍歷攻擊。path.replace("/", File.separator) 易漏邊界情況,改用 Paths.get()。| 誤區 | 問題 | 正確做法 |
|---|---|---|
Windows 用 \\ 轉義 | 字串需雙重轉義 \\\\,易出錯 | 用 File.separator 或 Path |
Linux 下硬編碼 / | Windows 失效 | 用 File.separator |
用 System.getProperty("line.separator") | Java 7 前可行,但可能拋 NPE | 用 System.lineSeparator() |
| 混淆 file.separator 與 path.separator | CLASSPATH 用錯分隔符導致類載入失敗 | 前者路徑層級,後者列表分隔 |
| 正則分割路徑未轉義 | split("\\") 報錯,split("/") 跨平台失效 | split(Pattern.quote(File.separator)) |
@Test
void testLineSeparator() {
String ls = System.lineSeparator();
if (System.getProperty("os.name").toLowerCase().contains("win")) {
assertEquals("\r\n", ls);
} else {
assertEquals("\n", ls);
}
}
@Test
void testPathJoin() {
String joined = SeparatorUtils.joinPath("a", "b", "c");
String expected = "a" + File.separator + "b" + File.separator + "c";
assertEquals(expected, joined);
}