java - ماهو - معنى final في الجافا




كيف أقوم بإنشاء سلسلة Java من محتويات الملف؟ (20)

قراءة كل النص من ملف

في ما يلي صيغة مضغوطة وقوية لـ Java 7 ، ملفوفة في طريقة الاستخدام:

static String readFile(String path, Charset encoding) 
  throws IOException 
{
  byte[] encoded = Files.readAllBytes(Paths.get(path));
  return new String(encoded, encoding);
}

قراءة أسطر النص من ملف

أضاف Java 7 طريقة ملائمة لقراءة ملف كخطوط نصية ، ممثلة في List<String> . هذا الأسلوب "lossy" لأن يتم فصل فواصل الأسطر من نهاية كل سطر.

List<String> lines = Files.readAllLines(Paths.get(path), encoding);

في Java 8 ، أضاف BufferedReader طريقة جديدة ، lines() لإنشاء Stream<String> . إذا تمت مصادفة IOException أثناء قراءة الملف ، فإنه يتم تغليفه في UncheckedIOException ، لأن Stream لا يقبل lambdas التي تضع الاستثناءات المحددة.

try (BufferedReader r = Files.newBufferedReader(path, encoding)) {
  r.lines().forEach(System.out::println);
}

توجد أيضًا طريقة Files.lines() التي تقوم Files.lines() شيء مشابه جدًا ، Files.lines() Stream<String> مباشرة. لكنني لا أحب ذلك. يحتاج Stream إلى استدعاء close() ؛ هذا غير موثق بشكل سيئ على واجهة برمجة التطبيقات ، وأظن أن العديد من الأشخاص لا يلاحظون حتى أن Stream يمتلك طريقة close() . لذا ستبدو شفرتك متشابهة جدًا ، مثل هذا:

try (Stream<String> lines = Files.lines(path, encoding)) {
  lines.forEach(System.out::println);
}

والفرق هو أن هناك Stream تم تعيينه لمتغير ، وأحاول تجنب ذلك كممارسة حتى لا أحاول استحضار الحدث مرتين.

استخدام الذاكرة

الطريقة الأولى ، التي تحافظ على فواصل الأسطر ، يمكن أن تتطلب ذاكرة عدة مرات حجم الملف مؤقتًا ، لأن لفترة قصيرة محتويات الملف الخام (صفيف بايت) ، والأحرف المشفرة (كل منها 16 بت حتى إذا تم تشفيرها كما 8 بت في الملف) الموجودة في الذاكرة في وقت واحد. من الأسلم تطبيق الملفات التي تعرف أنها صغيرة بالنسبة إلى الذاكرة المتوفرة.

الطريقة الثانية ، قراءة الأسطر ، عادة ما تكون أكثر كفاءة في الذاكرة ، لأن المخزن المؤقت للبايت المدخلات لفك التشفير لا يحتاج إلى احتواء الملف بأكمله. ومع ذلك ، فإنه لا يزال غير مناسب للملفات كبيرة جدًا بالنسبة للذاكرة المتوفرة.

لقراءة الملفات الكبيرة ، تحتاج إلى تصميم مختلف للبرنامج الخاص بك ، واحد يقرأ جزءًا من النص من جدول ، ويعالجه ، ثم ينتقل إلى التالي ، ويعيد استخدام نفس كتلة الذاكرة ذات الحجم الثابت. هنا ، يعتمد "كبير" على مواصفات الكمبيوتر. في الوقت الحاضر ، قد تكون هذه العتبة غيغا بايت من ذاكرة الوصول العشوائي. الطريقة الثالثة ، باستخدام Stream<String> هي إحدى الطرق للقيام بذلك ، إذا كانت "سجلات" الإدخال الخاصة بك تحدث خطوطًا فردية. (باستخدام الأسلوب readLine() من BufferedReader هو مكافئ إجرائي لهذا الأسلوب.)

ترميز الحروف

شيء واحد مفقود من العينة في المشاركة الأصلية هو ترميز الأحرف. هناك بعض الحالات الخاصة التي يكون فيها النظام الأساسي الافتراضي هو ما تريده ، ولكنها نادرة ، ويجب أن تكون قادرًا على تبرير اختيارك.

تعرّف فئة StandardCharsets بعض الثوابت للتشفيرات المطلوبة لكل أوقات تشغيل Java:

String content = readFile("test.txt", StandardCharsets.UTF_8);

يتوفر النظام الأساسي للنظام الأساسي من فئة Charset نفسها:

String content = readFile("test.txt", Charset.defaultCharset());

ملاحظة: تستبدل هذه الإجابة إلى حد كبير إصدار جافا 6 الخاص بي. تقوم الأداة المساعدة Java 7 بأمان على تبسيط التعليمات البرمجية ، وتمنع الإجابة القديمة ، التي تستخدم مخزنًا مؤقتًا معينًا ، الملف الذي تمت قراءته من أن يتم حذف المخزن المؤقت المعين. يمكنك عرض النسخة القديمة عبر الرابط "المحرر" في هذه الإجابة.

لقد تم استخدام المصطلح أدناه لبعض الوقت الآن. ويبدو أنها الأكثر انتشارًا ، على الأقل على المواقع التي زرتها.

هل هناك طريقة أفضل / مختلفة لقراءة ملف إلى سلسلة في جاوة؟

private String readFile(String file) throws IOException {
    BufferedReader reader = new BufferedReader(new FileReader (file));
    String         line = null;
    StringBuilder  stringBuilder = new StringBuilder();
    String         ls = System.getProperty("line.separator");

    try {
        while((line = reader.readLine()) != null) {
            stringBuilder.append(line);
            stringBuilder.append(ls);
        }

        return stringBuilder.toString();
    } finally {
        reader.close();
    }
}

أيضًا إذا كان ملفك موجودًا داخل وعاء ، يمكنك أيضًا استخدام هذا:

public String fromFileInJar(String path) {
    try ( Scanner scanner 
            = new Scanner(getClass().getResourceAsStream(path))) {
        return scanner.useDelimiter("\\A").next();
    }
}

يجب أن يبدأ المسار بـ / على سبيل المثال إذا كانت جرة الخاص بك

my.jar/com/some/thing/a.txt

ثم تريد استحضاره على النحو التالي:

String myTxt = fromFileInJar("/com/com/thing/a.txt");

إذا كنت تبحث عن بديل لا يتضمن مكتبة طرف ثالث (مثل I / O ) ، يمكنك استخدام فئة Scanner :

private String readFile(String pathname) throws IOException {

    File file = new File(pathname);
    StringBuilder fileContents = new StringBuilder((int)file.length());
    Scanner scanner = new Scanner(file);
    String lineSeparator = System.getProperty("line.separator");

    try {
        while(scanner.hasNextLine()) {
            fileContents.append(scanner.nextLine() + lineSeparator);
        }
        return fileContents.toString();
    } finally {
        scanner.close();
    }
}

إذا كنت تحتاج إلى معالجة سلسلة (معالجة متوازية) فإن Java 8 تحتوي على Stream API الرائع.

String result = Files.lines(Paths.get("file.txt"))
                    .parallel() // for parallel processing 
                    .map(String::trim) // to change line   
                    .filter(line -> line.length() > 2) // to filter some lines by a predicate                        
                    .collect(Collectors.joining()); // to join lines

تتوفر المزيد من الأمثلة في نماذج عينات JDK sample/lambda/BulkDataOperations التي يمكن تنزيلها من صفحة تحميل Oracle Java SE 8

مثال آخر على الخطوط الملاحية المنتظمة

String out = String.join("\n", Files.readAllLines(Paths.get("file.txt")));

استخدام الكود:

File file = new File("input.txt");
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(
                file));
byte[] buffer = new byte[(int) file.length()];
bin.read(buffer);
String fileStr = new String(buffer);

fileStr يحتوي على الإخراج في سلسلة.


استنادًا إلى إجابة erickson ، يمكنك استخدام:

public void readAll(String fileName) throws IOException {
    List<String> lines = Files.readAllLines(new File(fileName).toPath());
    return String.join("\n", lines.toArray(new String[lines.size()]));
}

باستخدام هذه المكتبة ، هو سطر واحد:

String data = IO.from(new File("data.txt")).toString();

بعد Ctrl + F'ing بعد الماسح الضوئي ، أعتقد أنه يجب إدراج حل الماسح الضوئي أيضًا. في الأسهل قراءة الموضة ، مثل:

public String fileToString(File file, Charset charset) {
  Scanner fileReader = new Scanner(file, charset);
  fileReader.useDelimiter("\\Z"); // \Z means EOF.
  String out = fileReader.next();
  fileReader.close();
  return out;
}

إذا كنت تستخدم Java 7 أو أحدث (ويجب عليك فعلًا) أن تفكر في استخدام التجريب مع الموارد لتسهيل قراءة التعليمات البرمجية. لا مزيد من الاشياء النقطه المغلقة كل شيء. ولكن هذا هو في الغالب اختيار أسلوب الأسلوب.

أنا نشر هذا في الغالب لإنهاء ، لأنه إذا كنت بحاجة إلى القيام بذلك كثيرا ، ينبغي أن يكون هناك أشياء في java.nio.file.Files التي ينبغي أن تقوم بالمهمة بشكل أفضل.

سيكون اقتراحي لاستخدام Files#readAllBytes(Path) للاستيلاء على جميع البايتات ، Files#readAllBytes(Path) إلى سلسلة جديدة (بايت [] Charset) للحصول على سلسلة من الذي يمكنك الوثوق به. سوف تكون Charsets تعني لك خلال حياتك ، لذا احذر من هذه الأشياء الآن.

أعطى آخرون رمز وأشياء ، وأنا لا أريد أن أسرق مجده. ؛)


حل مرن يستخدم IOUtils من Apache commons-io بالاشتراك مع StringWriter :

Reader input = new FileReader();
StringWriter output = new StringWriter();
try {
  IOUtils.copy(input, output);
} finally {
  input.close();
}
String fileContents = output.toString();

وهو يعمل مع أي قارئ أو دفق إدخال (ليس فقط مع الملفات) ، على سبيل المثال عند القراءة من عنوان URL.


سيعمل هذا الرمز على تطبيع فواصل الأسطر ، والتي قد تكون أو لا تكون ما تريد فعله حقًا.

هنا هو البديل الذي لا يفعل ذلك ، والذي هو (IMO) أبسط لفهم من رمز NIO (على الرغم من أنه لا يزال يستخدم java.nio.charset.Charset ):

public static String readFile(String file, String csName)
            throws IOException {
    Charset cs = Charset.forName(csName);
    return readFile(file, cs);
}

public static String readFile(String file, Charset cs)
            throws IOException {
    // No real need to close the BufferedReader/InputStreamReader
    // as they're only wrapping the stream
    FileInputStream stream = new FileInputStream(file);
    try {
        Reader reader = new BufferedReader(new InputStreamReader(stream, cs));
        StringBuilder builder = new StringBuilder();
        char[] buffer = new char[8192];
        int read;
        while ((read = reader.read(buffer, 0, buffer.length)) > 0) {
            builder.append(buffer, 0, read);
        }
        return builder.toString();
    } finally {
        // Potential issue here: if this throws an IOException,
        // it will mask any others. Normally I'd use a utility
        // method which would log exceptions and swallow them
        stream.close();
    }        
}

في سطر واحد (Java 8) ، بافتراض أن لديك قارئ:

String sMessage = String.join("\n", reader.lines().collect(Collectors.toList()));

كن على دراية عند استخدام fileInputStream.available() ليس لدى العدد الصحيح الذي تم إرجاعه لتمثيل حجم الملف الفعلي ، ولكن بالأحرى مقدار التخمين للبايتات يجب أن يكون النظام قادراً على القراءة من الدفق بدون حظر IO. طريقة آمنة وبسيطة يمكن أن تبدو مثل هذا

public String readStringFromInputStream(FileInputStream fileInputStream) {
    StringBuffer stringBuffer = new StringBuffer();
    try {
        byte[] buffer;
        while (fileInputStream.available() > 0) {
            buffer = new byte[fileInputStream.available()];
            fileInputStream.read(buffer);
            stringBuffer.append(new String(buffer, "ISO-8859-1"));
        }
    } catch (FileNotFoundException e) {
    } catch (IOException e) { }
    return stringBuffer.toString();
}

يجب اعتبار أن هذا الأسلوب غير مناسب لترميزات الأحرف المتعددة البايت مثل UTF-8.


لقراءة ملف باسم ثنائي وتحويل في النهاية

public static String readFileAsString(String filePath) throws IOException {
    DataInputStream dis = new DataInputStream(new FileInputStream(filePath));
    try {
        long len = new File(filePath).length();
        if (len > Integer.MAX_VALUE) throw new IOException("File "+filePath+" too large, was "+len+" bytes.");
        byte[] bytes = new byte[(int) len];
        dis.readFully(bytes);
        return new String(bytes, "UTF-8");
    } finally {
        dis.close();
    }
}

مع Java 7 ، هذا هو خياري المفضل لقراءة ملف UTF-8:

String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");

منذ Java 7 ، يحتوي JDK على واجهة برمجة التطبيقات java.nio.file الجديدة ، التي توفر العديد من الاختصارات ، لذلك لا تكون مكتبات الطرف الثالث مطلوبة دائمًا لعمليات الملفات البسيطة.


هذا واحد يستخدم طريقة RandomAccessFile.readFully ، يبدو أنه متاح من JDK 1.0!

public static String readFileContent(String filename, Charset charset) throws IOException {
    RandomAccessFile raf = null;
    try {
        raf = new RandomAccessFile(filename, "r");
        byte[] buffer = new byte[(int)raf.length()];
        raf.readFully(buffer);
        return new String(buffer, charset);
    } finally {
        closeStream(raf);
    }
} 


private static void closeStream(Closeable c) {
    if (c != null) {
        try {
            c.close();
        } catch (IOException ex) {
            // do nothing
        }
    }
}

يمكنك محاولة فاحص "الماسح الضوئي والملفات" ، وهو حل قليل الأسطر

 try
{
  String content = new Scanner(new File("file.txt")).useDelimiter("\\Z").next();
  System.out.println(content);
}
catch(FileNotFoundException e)
{
  System.out.println("not found!");
}

Guava لديها طريقة مشابهة لتلك التي من العموم IOUtils التي ذكرها Willi aus Rohr:

import com.google.common.base.Charsets;
import com.google.common.io.Files;

// ...

String text = Files.toString(new File(path), Charsets.UTF_8);

تحرير بواسطة أوسكار رييس

هذه هي التعليمات البرمجية الأساسية (المبسطة) على المكتبة المذكورة:

InputStream in = new FileInputStream(file);
byte[] b  = new byte[file.length()];
int len = b.length;
int total = 0;

while (total < len) {
  int result = in.read(b, total, len - total);
  if (result == -1) {
    break;
  }
  total += result;
}

return new String( b , Charsets.UTF_8 );

تحرير (بواسطة Jonik): لا يطابق ما ورد أعلاه شفرة المصدر الخاصة بنسخ الجوافة الحديثة. بالنسبة إلى المصدر الحالي ، اطلع على الفئات Files و CharStreams و ByteSource و CharSource في الحزمة com.google.common.io .


String content = new String(Files.readAllBytes(Paths.get("readMe.txt")));

منذ جافا 7 يمكنك القيام بذلك بهذه الطريقة.


import java.nio.file.Files;

.......

 String readFile(String filename) {
            File f = new File(filename);
            try {
                byte[] bytes = Files.readAllBytes(f.toPath());
                return new String(bytes,"UTF-8");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
    }

public static String slurp (final File file)
throws IOException {
    StringBuilder result = new StringBuilder();

    try {
        BufferedReader reader = new BufferedReader(new FileReader(file));

        char[] buf = new char[1024];

        int r = 0;

        while ((r = reader.read(buf)) != -1) {
            result.append(buf, 0, r);
        }
    }
    finally {
        reader.close();
    }

    return result.toString();
}




io