Java BufferedWriter Создание нулевых символов

Я использовал Java BufferedWriter для записи в файл, чтобы проанализировать некоторые входные данные. Однако, когда я открываю файл после, кажется, что добавлены нулевые символы. Я попытался указать кодировку как «US-ASCII» и «UTF8», но получил тот же результат. Вот мой фрагмент кода:

Scanner fileScanner = new Scanner(original);
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "US-ASCII"));
while(fileScanner.hasNextLine())
  {
     String next = fileScanner.nextLine();
     next = next.replaceAll(".*\\x0C", ""); //remove up to ^L
     out.write(next);
     out.newLine();
  }
 out.flush();
 out.close();

Может быть, проблема даже не в BufferedWriter?

Я сузил его до этого блока кода, потому что, если я закомментирую его, в выходном файле не будет нулевых символов. Если я выполняю замену регулярных выражений в VIM, файл не содержит нулевых символов (:%s/.*^L//g).

Дайте мне знать, если вам нужна дополнительная информация.

Спасибо!

EDIT: шестнадцатеричный дамп обычной строки выглядит так: 0000000 5349 2a41 3030 202a

Но когда этот код запускается, шестнадцатеричный дамп выглядит так: 0000000 5330 2a49 4130 202a

Я не уверен, почему все смешивается.

EDIT: Кроме того, даже если файл не соответствует регулярному выражению и проходит через этот блок кода, он содержит нулевые символы.

EDIT: Вот шестнадцатеричный дамп первых нескольких строк diff: http://pastie.org/pastes/8964701/text

команда была такой: diff -y testfile.hexdump ожидаемый вывод.hexdump

Остальные строки отличаются от последних двух.


person SortingHat    schedule 19.03.2014    source источник
comment
Какие данные являются вводными? Это обычный текст с известной кодировкой символов? Вы уверены, что открываете его с этой кодировкой? Исчезают ли ложные байты NULL, если вы закомментируете строку replaceAll?   -  person 5gon12eder    schedule 23.03.2014
comment
Это обычный текстовый файл ASCII. Похоже, каждый раз, когда этот блок запускается, происходит что-то странное. Я сравнил шестнадцатеричные дампы файла без заголовков и файла, пропущенного через этот код для удаления заголовков, и похоже, что он меняет местами байты. Я добавил пример выше.   -  person SortingHat    schedule 24.03.2014
comment
Можно ли получить копию входного файла, который вы используете?   -  person Jason Nichols    schedule 24.03.2014
comment
К сожалению нет. Я не могу выдать информацию.   -  person SortingHat    schedule 24.03.2014
comment
Я добавил частичный шестнадцатеричный дамп выше. Остальные строки другие, и файл, проходящий через этот код, на самом деле тоже короче.   -  person SortingHat    schedule 24.03.2014
comment
Единственная разница между двумя шестнадцатеричными дампами заключается в том, что один имеет окончание строки LF (0A), а другой — CRLF (0D 0A). Остальные данные сдвигаются вперед для размещения дополнительного байта.   -  person Stuart Caie    schedule 24.03.2014
comment
@StuartCaie Вот в чем проблема! Если вы создадите ответ с этим, я отмечу его как правильный. Я думаю, мне нужно быть более наблюдательным со своими шестнадцатеричными дампами.   -  person SortingHat    schedule 24.03.2014
comment
Что это за нулевые символы, на которые вы ссылаетесь ?? В вашем шестнадцатеричном дампе нет байтов с нулевым значением, поэтому ваша постановка задачи кажется ошибочной.   -  person Chris Stratton    schedule 28.03.2014
comment
да. Моя первоначальная догадка была ошибочной. Просмотр файла через текстовый редактор показал странные символы, но это было потому, что все было смещено из-за отсутствующих окончаний строк. Так или иначе, это было решено. См. ответ @StuartCaie.   -  person SortingHat    schedule 28.03.2014


Ответы (4)


РЕДАКТИРОВАТЬ: Глядя на приведенный вами шестнадцатеричный diff, единственная разница заключается в том, что у одного есть окончания строки LF (0A), а у другого - окончания строки CRLF (0D 0A). Все остальные данные в вашем diff сдвигаются вперед, чтобы вместить дополнительный байт.

CRLF — это строка по умолчанию, заканчивающаяся в используемой вами ОС. Если вы хотите, чтобы в вашем выводе заканчивалась определенная строка, напишите строку "\n" или "\r\n".

Ранее я заметил, что Scanner не указывает кодировку. Он должен указывать соответствующий, в котором, как известно, закодирован ввод. Однако это не является источником неожиданного вывода.

person Stuart Caie    schedule 24.03.2014
comment
Я пробовал как UTF-8, так и ASCII, но безуспешно. Я добавил частичный шестнадцатеричный дамп выше. Последние несколько строк отличаются, а остальные строки отличаются в зависимости от diff. Файл, который выполнялся через этот код, по какой-то причине также имеет более короткий шестнадцатеричный дамп. - person SortingHat; 24.03.2014
comment
Спасибо за помощь! Мне нужно быть более наблюдательным со своими шестнадцатеричными дампами. - person SortingHat; 25.03.2014


Я думаю, что происходит следующее

Все строки, содержащие ^L (ff), изменяются, чтобы удалить все до ^L, но, кроме того, у вас есть побочный эффект в 1, который также удаляет все \r (cr). Однако, если cr появляется перед ^L nextLine(), это также рассматривается как строка. Обратите внимание, как в выходном файле ниже количество cr + nl равно 6 во входном файле, а количество cr + nl также равно 6, но все они nl, поэтому строка с c сохраняется, потому что она обрабатывается на строка, отличная от ^L. Вероятно, это не то, что вы хотите. Смотри ниже.

Некоторые наблюдения

  1. Исходный файл создается в системе, которая использует \r\n для определения новой строки, а ваша программа запускается в системе, которая этого не делает. Из-за этого все вхождения 0xd будут удалены. Это сделает два файла разных размеров, даже если нет ^L.

  2. Но вы, вероятно, упустили из виду № 1, потому что vim будет работать в режиме DOS (распознает \r\n как разделитель новой строки) или в не-DOS-режиме (только \n) в зависимости от того, что он читает при открытии файла и скрывает этот факт от пользователя. пользователь, если это возможно. На самом деле, чтобы проверить, мне пришлось использовать грубую силу в \r, используя ^v^m, потому что я редактировал в Linux, используя vim подробнее здесь.

  3. Ваши средства для тестирования, вероятно, используют od -x (для шестнадцатеричного права)? Но это выводит целые числа, а это не то, что вам нужно. Рассмотрим следующий входной файл и выходной файл. После запуска вашей программы. Как показано в vi

Входной файл

a
b^M
c^M^M ^L
d^L

Выходной файл

a
b
c

Ну, может быть, это правильно, давайте посмотрим, что скажет од

od -x входного файла

0a61    0d62    630a    0d0d    0c20    640a    0a0c 

od -x выходного файла

0a61    0a62    0a63    0a0a    000a

А откуда взялся этот ноль? Но подождите от man-страницы od

-t type     Specify the output format.  type is a string containing one or more of the following kinds of type specifiers:

   q          a       Named characters (ASCII).  Control characters are displayed using the following names:
-h, -x      Output hexadecimal shorts.  Equivalent to -t x2.
-a          Output named characters.  Equivalent to -t a.

О, хорошо, вместо этого используйте опцию -a

od -a ввода

a  nl   b  cr  nl   c  cr  cr  sp  ff  nl   d  ff  nl

od -a вывода

a  nl   b  nl   c  nl  nl  nl  nl 

Заставить java игнорировать \r

И, наконец, все, что было сказано, вам действительно нужно преодолеть неявное понимание java, что \r разделяет строку, даже вопреки документации. Даже при явной настройке сканера на использование шаблона игнорирования \r он по-прежнему работает вопреки документации, и вы должны переопределить это снова, установив разделитель (см. ниже). Я обнаружил, что следующее, вероятно, сделает то, что вы хотите, настаивая на семантике строк Unix. Я также добавил некоторую логику, чтобы не выводить пустую строку.

public static void repl(File original,File file) throws IOException
{
   Scanner fileScanner = new Scanner(original);
   Pattern pattern1 = Pattern.compile("(?d).*");

   fileScanner.useDelimiter("(?d)\\n");

   BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));

   while(fileScanner.hasNext(pattern1))
   {
      String next = fileScanner.next(pattern1);

      next = next.replaceAll("(?d)(.*\\x0C)|(\\x0D)","");
      if(next.length() != 0)
      {
         out.write(next);
         out.newLine();
      }
   }
   out.flush();
   out.close();
}

С этим изменением вывод выше изменится на.

od -a ввода

a  nl   b  cr  nl   c  cr  cr  sp  ff  nl   d  ff  nl

od -a вывода

a  nl   b  nl
person waTeim    schedule 24.03.2014

Стюарт Кей дал ответ. если вы ищете код, чтобы избежать этих символов.

Основная проблема заключается в том, что файл Org использует другой разделитель строк, а новый файл использует другой символ разделителя строк.

Один из простых способов — найти символ-разделитель файлов Org и использовать его в новом файле.

    try(BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            Scanner fileScanner = new Scanner(original);) {
        String lineSep = null;
        boolean lineSepFound = false;
        while(fileScanner.hasNextLine())
        {

            if (!lineSepFound){
                MatchResult matchResult = fileScanner.match();
                if (matchResult != null){
                    lineSep = matchResult.group(1);
                    if (lineSep != null){
                        lineSepFound = true;
                    }
                }
            }else{
                out.write(lineSep);
            }
            String next = fileScanner.nextLine();
            next = next.replaceAll(".*\\x0C", ""); //remove up to ^L
            out.write(next);

        }
    } catch ( IOException e) {
        e.printStackTrace();
    }

Примечание. ** MatchResult matchResult = fileScanner.match(); предоставит результат matchResult для последнего выполненного совпадения. И в нашем случае мы использовали hasNextLine() - сканер использовал linePattern для поиска следующей строки.. Scanner.hasNextLine Исходный код для поиска разделителя строк ,

но, к сожалению, нет способа вернуть разделитель строк. Поэтому я использовал их код, чтобы получить строкуSep только один раз. и использовал этот lineSep для создания нового файла.

Также в соответствии с вашим кодом у вас будет дополнительный разделитель строк в конце файла. Исправил здесь.

Дай мне знать, если это работает.

person Mani    schedule 26.03.2014