Как сохранить пробелы при чтении текстового файла фиксированной длины

Я читаю файл .txt, который, по сути, представляет собой файл типа csv со строками и столбцами, только столбцы разделены буквальными символами [пробел]. Каждый столбец имеет предопределенную длину этих символов, указанную в файле определения данных.

Образец текстового файла

Ниже то, что я пробовал, но игнорирует пробелы. Цифры — это то, что мне дали, чтобы различать столбцы.

function formatTextFile(input){
    var readFile = fileOpen(input,"read");
    var line1 = FileReadLine(readFile);
    var line2 = FileReadLine(readFile);
    line2 = REReplace(line2,"[^(.{7}.{10}.{47}.{34}.{14}.{13}.{15}.{22}.{28}.{18}.{2})]+[ ]", "|", "all");
    return line2;
}

This is the output from that:

|1. 16-1268|5/2/201|31530|$0.00|404|AP 

Я думал о попытке использовать java для форматирования строк. Я не понял этого.


person Matt Wilde    schedule 17.06.2016    source источник
comment
Как вы думаете, почему ColdFusion игнорирует пробелы при чтении файла?   -  person Dan Bracuk    schedule 18.06.2016
comment
Я изменил вопрос и заголовок, чтобы лучше описать проблему. Регулярное выражение, вероятно, изменяет пробел. Регулярные выражения не моя сильная сторона, но вы можете попробовать обратные ссылки: REReplace(text,"^(.{7})..etc...(.{2})\s+$", "\1|..etc..|\11");   -  person Leigh    schedule 18.06.2016
comment
Из любопытства, что вы в конечном итоге делаете с результатами?   -  person Leigh    schedule 19.06.2016
comment
@danbracuk, когда я прочитал строку файла, возвращенная переменная сократила строку до всех одиночных пробелов между «столбцами». Пробелы определенно есть в исходном файле, я просто не знаю, как с ними справляется coldfusion.   -  person Matt Wilde    schedule 20.06.2016
comment
@Leigh помещая это в объект запроса   -  person Matt Wilde    schedule 20.06.2016
comment
RE: когда я сделал строку чтения файла, это не строка чтения, удаляющая все пробелы. Это регулярное выражение (дамп line2 перед заменой). Вам нужно другое выражение. Как я уже упоминал выше, вы можете использовать обратные ссылки, но... лично я предпочитаю строковые функции, как указано в ответе ниже. RE: поместить его в объект запроса Для отображения (а не для вставки в базу данных), верно?   -  person Leigh    schedule 20.06.2016
comment
@Leigh да, строковые функции - это то, что нужно. Однако я сделал writeDump строки чтения до того, как сделал какое-либо регулярное выражение, и все равно не осталось пробелов. Исключает ли writeDump лишние пробелы?   -  person Matt Wilde    schedule 20.06.2016
comment
@MattWilde — по умолчанию он генерирует html, который сворачивает пробелы. Либо используйте теги format="text", либо старые школьные теги writeOutput и <pre>. Оба отображали исходное белое пространство для меня.   -  person Leigh    schedule 20.06.2016
comment
(Правка) Тьфу... мозговой сбой. Я хотел ввести подвыражения, а не обратные ссылки. Кстати, если результаты вставляются в базу данных (а не отображаются на экране), есть лучшие варианты.   -  person Leigh    schedule 20.06.2016


Ответы (3)


Зачем вообще возиться с регулярными выражениями, если у вас фиксированная ширина столбцов? Рассмотрим следующее:

<cfscript>

    line = "ab  12  xy 654   +1234    ";
    columnLengths = [4, 4, 3, 6, 1, 8];

    o = readFixedLine(line, columnLengths);

    function readFixedLine(line, columnLengths) {

        var cells = [];
        var offset = 1;

        for (length in columnLengths) {
            cells.add( mid(line, offset, length) );
            offset += length;
        }

        return cells;
    }

    // cells read (the dump doesn't display whitespaces)
    writeDump(o);
    /*
        whitespaces represented as asterisks
        [1] ab**
        [2] 12**
        [3] xy*
        [4] 654***
        [5] +
        [6] 1234****
    */

    // proving that whitespaces are kept
    for (entry in o) {
        writeOutput( len(entry) & "<br>" );
    }
    /*
        4
        4
        3
        6
        1
        8
    */

</cfscript>

Функция mid безотказна (в этом отношении), поэтому вам даже не нужно беспокоиться о пустых строках или отсутствующих столбцах. Однако fileReadLine обрезает конечные пробелы, так что вам здесь не повезло. Здесь может помочь только Java (прочитайте свой файл с помощью Stream или BufferedReader).

person Alex    schedule 18.06.2016

Спасибо @Alex за отличное решение. Java — это то, что нужно, и его метод очень функционален. Я просто хочу показать, как я реализовал его при построении запроса.

Осталось с массивом столбцов и строк, которые вы можете поместить в объект запроса с помощью QueryAddColumn. Мне пришлось возиться с длиной столбца, но метод java - это то, что нужно. Еще раз спасибо @Alex.

function formatTextFile(input){
        var fileReader = createObject("java","java.io.FileReader").init(input);
        var bufferedReader = createObject("java","java.io.BufferedReader").init(fileReader);
        var lineReader = createObject("java","java.io.LineNumberReader").init(bufferedReader);

        var columnLengths = [7,10,47,34,14,13,16,22,75,18,2];
        var rows = ArrayNew(2);
        var line = lineReader.readLine();
        //skip empty lines
        while(!structKeyExists(Local,"line")&&!len(trim(Local.line))){
            line = lineReader.readLine();
        }

        while(isDefined("line")){
            var cells = ArrayNew(1);
            var offset = 1;
            for(var i = 1; i<=ArrayLen(columnLengths); i++){
                cells.add(mid(line,offset,columnLengths[i]));
                offset+=columnLengths[i];
                ArrayAppend(rows[i],cells[i]);
            }
            line = lineReader.readLine();
        }
        return rows;        
    }

Изображение вывода

person Matt Wilde    schedule 20.06.2016
comment
(Редактировать) Хорошая работа. Хотя, если вы используете CF9+, лучше использовать область видимости Local и structKeyExists(Local, "line") вместо IsDefined("line"). В противном случае возможны неожиданные результаты, если такое же имя переменной существует в какой-либо другой области (переменные, URL-адрес, форма и т. д.). Кроме того, не уверен, что первый цикл while действительно нужен. - person Leigh; 20.06.2016
comment
@Leigh Хороший вопрос с областью применения. Мы используем Framework/1 на нашем сервере, что может не иметь значения, но я не привык использовать эти области в контроллерах. Наверное лучшая практика. Я полагал, что первый цикл while будет пропускать пустые строки. но вместо этого я, вероятно, должен сделать if(!len(line)){} проверку? - person Matt Wilde; 20.06.2016
comment
Да, он не пропускает пустые строки. Он возвращает все, что есть (может быть пустой строкой) ИЛИ null, когда достигает EOF. Чтобы найти первую непустую строку, убедитесь, что она существует, затем проверьте длину: structKeyExists(Local, "line") && !len(Local.line). Также можно добавить туда trim(), если вы хотите игнорировать все пробельные строки. - person Leigh; 20.06.2016

(Слишком долго для комментариев)

Примечание к исходному вопросу: пробел удаляется регулярным выражением, а не FileReadLine. Хотя лично я нахожу другое подходит гораздо более читабельно - технически это можно сделать с помощью регулярного выражения.

Например, вы можете создать выражение, которое соответствует всей строке, и использовать несколько подвыражений для соответствия желаемому количеству символов в каждом поле. Затем используйте сгенерированные обратные ссылки для создания строки с разделителями каналов, т.е. \1|\2|\3 ....

Код: Протестировано с помощью CF11

// ^ - starting with
// $ - ending with
// ( ) - subexpression or capturing group
// \1 - backreference, 1 corresponds to first subexpression
result = REReplace(line1
           ,"^(.{7})(.{10})(.{47})(.{34})(.{14})(.{13})(.{15})(.{22})(.{28})(.{18})(.{2})\s+$"
           , "\1|\2|\3|\4|\5|\6|\7|\8|\9|\10|\11"
        );

Пример данных:

A1     B         C                                              D                                 E             F            G              H                     I                           J                 K   
A2     B         C                                              D                                 E             F            G              H                     I                           J                 K   
A3     B         C                                              D                                 E             F            G              H                     I                           J                 K  

Если вы выгрузите результаты и проверите len() каждого поля, вы увидите, что пробелы сохранены. Так что проблема в исходном выражении.

for (currValue in listToArray(line1, "|")) {
    writeOutput("<br>["& len(currValue) &"] "& currValue );
}

Результаты:

[7 ] A1     |
[10] B         |
[47] C                                              |
[34] D                                 |
[14] E             |
[13] F            |
[15] G              |
[22] H                     |
[28] I                           |
[18] J                 |
[2 ] K |
person Leigh    schedule 20.06.2016
comment
Никогда не использовал это с регулярным выражением, хотя классная функциональность. Теперь я понимаю это с пробелами и writeDump(). Кроме того, я пропустил построение запроса и использовал только массивы для помещения данных в структуру. который сериализуется в json и передается другому отделу для ввода данных. каковы будут ваши предложения по записи в базу данных - person Matt Wilde; 21.06.2016
comment
Зависит от вашей настройки. Если файл просто нужно вставить в базу данных, доступную для CF, обычно проще использовать инструменты загрузки вашей базы данных. Например, BULK INSERT в sql-сервере, LOAD DATA в mySQL и т. д. - person Leigh; 21.06.2016