Как избежать только разделителя, а не символа новой строки в CSV - PullRequest
0 голосов
/ 04 апреля 2020

Я получаю обычные CSV-файлы, разделенные запятыми, с данными, имеющими символ новой строки.

Входные данные

Я хочу преобразовать входные данные в:

  1. Pipe (| ) с разделителями
  2. Без кавычек для экранирования ("или")
  3. Трубка (|) внутри данных, экранированных символом вставки (^)

Мой файл может также содержат несколько строк данных (или данных в новой строке в одной строке).

Ожидаемые выходные данные

Выходной файл I смог сгенерировать.

Output Data

Как вы можете видеть на изображении, каретка (^) отлично экранировала все каналы (|) в данных, но также экранирование символа новой строки в 5-й и 6-й строке, что мне не нужно.

ПРИМЕЧАНИЕ. Все символы возврата каретки (\ r или CR) и символы новой строки (\ n, LF) должны быть такими, как есть. как показано на изображениях.

import csv
import sys

inputPath = sys.argv[1]
outputPath = sys.argv[2]
with open(inputPath, encoding="utf-8") as inputFile:
    with open(outputPath, 'w', newline='', encoding="utf-8") as outputFile:
        reader = csv.DictReader(inputFile, delimiter=',')
        writer = csv.DictWriter(
            outputFile, reader.fieldnames, delimiter='|', quoting=csv.QUOTE_NONE, escapechar='^', doublequote=False, quotechar="")
        writer.writeheader()
        writer.writerows(reader)

print("Formationg complete.")

Приведенный выше код был написан на Python, было бы здорово, если бы я мог получить помощь на Python. Ответы на других языках программирования также принимаются.

Терм Более 8 миллионов записей

Ниже приведены некоторые примеры данных:

"VENDOR ID","VENDOR NAME","ORGANIZATION NUMBER","ADDRESS 1","CITY","COUNTRY","ZIP","PRIMARY PHONE","FAX","EMAIL","LMS RECORD CREATED DATE","LMS RECORD MODIFY DATE","DELETE FLAG","LMS RECORD ID"
"a0E6D000001Fag8UAC","Test 'Vendor' 1","","This Vendor contains a single (') quote.","","","","","","test@test.com","2020-4-1 06:32:29","2020-4-1 06:34:43","false",""
"a0E6D000001FagDUAS","Test ""Vendor"" 2","","This Vendor contains a double("") quote.","","","","","","test@test.com","2020-4-1 06:33:38","2020-4-1 06:35:18","false",""
"a0E6D000001FagIUAS","Test Vendor | 3","","This Vendor contains a Pipe (|).","","","","","","test@test.com","2020-4-1 06:38:45","2020-4-1 06:38:45","false",""
"a0E6D000001FagNUAS","Test Vendor 4","","This Vendor contains a
carriage return, i.e 
data in new line.","","","","","","test@test.com","2020-4-1 06:43:08","2020-4-1 06:43:08","false",""

ПРИМЕЧАНИЕ. Если вы копируете вышеуказанные данные, убедитесь, что 5-я и 6-я строки должны заканчиваться только LF (т. е. Новая строка, \ n), как показано на изображениях, или, пожалуйста, попробуйте повторить эти 2 строки, поскольку именно в этом и заключается вопрос о том, чтобы не избежать этих 2 строк специально, как показано на рисунке ниже.

Приведенный выше код является окончательным результатом всех моих выводов по inte rnet. Я даже попробовал библиотеку pandas, и ее окончательный вывод такой же.

Ответы [ 2 ]

0 голосов
/ 10 апреля 2020

Еще одна альтернатива тому, чего я хочу достичь, я сделал с помощью скрипта Wondows Powershell.

((Get-Content -path $args[0] -Raw) -replace '\|', '^|') | Set-Content -NoNewline -Force -Path $args[0]
((Get-Content -path $args[0] -Raw) -replace '^"', '') | Set-Content -NoNewline -Force -Path $args[0]
((Get-Content -path $args[0] -Raw) -replace "`"\r\n$", "") | Set-Content -NoNewline -Force -Path $args[0]
((Get-Content -path $args[0] -Raw) -replace '"\r\n"', "`r`n") | Set-Content -NoNewline -Force -Path $args[0]
((Get-Content -path $args[0] -Raw) -replace '","', '|') | Set-Content -NoNewline -Force -Path $args[0]
((Get-Content -path $args[0] -Raw) -replace '""', '"' ) | Set-Content -Path $args[0]

Способы выполнения:

  1. Использование Powershell

    replace.ps1 ''

  2. Использование пакетного сценария

    C: \ Windows \ System32 \ WindowsPowerShell \ v1.0 \ powershell.exe -ExecutionPolicy ByPass -команда "& ' \ replace.ps1' ' .csv'"

ПРИМЕЧАНИЕ: требуется Powershell V5.0 или выше

Это может обработать 1 миллион записей в минуту или около того.

Я понял, что нам нужно разделить громоздкие csv-файлы, чтобы умножить файл на 1 миллион записей каждый, а затем обработать их все по отдельности.

Пожалуйста, исправьте меня, если я ошибаюсь, или есть другая альтернатива.

0 голосов
/ 07 апреля 2020

Приведенный ниже код является просто альтернативным способом получения ожидаемого результата, но проблема все еще существует, поскольку выполнение этого сценария занимает вечность (более 12 часов) (и все еще не завершается, в конечном счете, я должен завершить процесс), когда работает на 9 миллионах записей.

Пакетная оболочка для кода VBS:

0</* :
    @echo off

        cscript /nologo /E:jscript "%~f0" %*

    exit /b %errorlevel% */0;

        var ARGS = WScript.Arguments;

        if (ARGS.Length < 3 ) {
            WScript.Echo("Wrong arguments");
            WScript.Echo(WScript.ScriptName + " path_to_file search replace [search replace[search replace [...]]]");
            WScript.Echo(WScript.ScriptName + " e?path_to_file search replace [search replace[search replace [...]]]");
            WScript.Echo("if filename starts with \"e?\" search and replace string will be evaluated for special characters ")
            WScript.Quit(1);
        }

        if (ARGS.Item(0).toLowerCase() == "-help" || ARGS.Item(0).toLowerCase() == "-h") {
            WScript.Echo(WScript.ScriptName + " path_to_file search replace [search replace[search replace [...]]]");
            WScript.Echo(WScript.ScriptName + " e?path_to_file search replace [search replace[search replace [...]]]");
            WScript.Echo("if filename starts with \"e?\" search and replace string will be evaluated for special characters ")
            WScript.Quit(0);
        }



        if (ARGS.Length % 2 !== 1 ) {
            WScript.Echo("Wrong arguments");
            WScript.Quit(2);
        }

        var jsEscapes = {
          'n': '\n',
          'r': '\r',
          't': '\t',
          'f': '\f',
          'v': '\v',
          'b': '\b'
        };


        //string evaluation
        //http://stackoverflow.com/questions/24294265/how-to-re-enable-special-character-sequneces-in-javascript

        function decodeJsEscape(_, hex0, hex1, octal, other) {
          var hex = hex0 || hex1;
          if (hex) { return String.fromCharCode(parseInt(hex, 16)); }
          if (octal) { return String.fromCharCode(parseInt(octal, 8)); }
          return jsEscapes[other] || other;
        }

        function decodeJsString(s) {
          return s.replace(
              // Matches an escape sequence with UTF-16 in group 1, single byte hex in group 2,
              // octal in group 3, and arbitrary other single-character escapes in group 4.
              /\\(?:u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([0-3][0-7]{0,2}|[4-7][0-7]?)|(.))/g,
              decodeJsEscape);
        }

        function convertToPipe(find, replace, str) {        
          return str.replace(new RegExp('\\|','g'),"^|");
        }

        function removeStartingQuote(find, replace, str) {      
          return str.replace(new RegExp('^"', 'g'), '');
        }

        function removeEndQuote(find, replace, str) {       
          return str.replace(new RegExp('"\r\n$', 'g'), '\r\n');
        }

        function removeLeadingAndTrailingQuotes(find, replace, str) {       
          return str.replace(new RegExp('"\r\n"', 'g'), '\r\n');
        }

        function replaceDelimiter(find, replace, str) {     
          return str.replace(new RegExp('","', 'g'), '|');
        }

        function convertSFDCDoubleQuotes(find, replace, str) {      
          return str.replace(new RegExp('""', 'g'), '"');
        }


      function getContent(file) {
            // :: http://www.dostips.com/forum/viewtopic.php?f=3&t=3855&start=15&p=28898  ::
            var ado = WScript.CreateObject("ADODB.Stream");
            ado.Type = 2;  // adTypeText = 2

            ado.CharSet = "iso-8859-1";  // code page with minimum adjustments for input
            ado.Open();
            ado.LoadFromFile(file);

            var adjustment = "\u20AC\u0081\u201A\u0192\u201E\u2026\u2020\u2021" +
                             "\u02C6\u2030\u0160\u2039\u0152\u008D\u017D\u008F" +
                             "\u0090\u2018\u2019\u201C\u201D\u2022\u2013\u2014" +
                             "\u02DC\u2122\u0161\u203A\u0153\u009D\u017E\u0178" ;


            var fs = new ActiveXObject("Scripting.FileSystemObject");
            var size = (fs.getFile(file)).size;

            var lnkBytes = ado.ReadText(size);
            ado.Close();
            var chars=lnkBytes.split('');
            for (var indx=0;indx<size;indx++) {
                if ( chars[indx].charCodeAt(0) > 255 ) {
                   chars[indx] = String.fromCharCode(128 + adjustment.indexOf(chars[indx]));
                }
            }
            return chars.join("");
       }

       function writeContent(file,content) {
            var ado = WScript.CreateObject("ADODB.Stream");
            ado.Type = 2;  // adTypeText = 2
            ado.CharSet = "iso-8859-1";  // right code page for output (no adjustments)
            //ado.Mode=2;
            ado.Open();

            ado.WriteText(content);
            ado.SaveToFile(file, 2);
            ado.Close();    
       }

        if (typeof String.prototype.startsWith != 'function') {
          // see below for better implementation!
          String.prototype.startsWith = function (str){
            return this.indexOf(str) === 0;
          };
        }


        var evaluate=false;
        var filename=ARGS.Item(0);
        if(filename.toLowerCase().startsWith("e?")) {
            filename=filename.substring(2,filename.length);
            evaluate=true;
        }
        var content=getContent(filename);
        var newContent=content;
        var find="";
        var replace="";

        for (var i=1;i<ARGS.Length-1;i=i+2){
            find=ARGS.Item(i);
            replace=ARGS.Item(i+1);
            if(evaluate){
                find=decodeJsString(find);
                replace=decodeJsString(replace);
            }
            newContent=convertToPipe(find,replace,newContent);
            newContent=removeStartingQuote(find,replace,newContent);        
            newContent=removeEndQuote(find,replace,newContent);
            newContent=removeLeadingAndTrailingQuotes(find,replace,newContent);
            newContent=replaceDelimiter(find,replace,newContent);       
            newContent=convertSFDCDoubleQuotes(find,replace,newContent);        
        }

        writeContent(filename,newContent);

Этапы выполнения:

> replace.bat <file_name or full_path_to_file> "." "."

Этот пакетный файл предназначен для любых целей. манипулирование файлом в соответствии с нашим требованием.

Я скомпилировал и сделал это из множества поисковых запросов Google. Это все еще в процессе, поскольку я жестко запрограммировал свои регулярные выражения в файле. Вы можете вносить изменения в соответствии с вашими потребностями в функции, которые я сделал, или даже создавать свои собственные функции, реплицируя другие функции и вызывая их в конце.

...