Чтение файла и сохранение в виде массива без пропуска пустых строк

Если вы не хотите использовать SQL, *Кроме того, вам будет сложно выполнять любые операции со схемой базы данных из сценария оболочки.

Вы можете написать приложение Pro *C (или что-то подобное ), использовать модули Perl DBIи DBD::Oracleили эквивалент на таком языке, как Python или PHP.

4
13.06.2021, 17:39
2 ответа

Поскольку \tявляется символом пробела(символом, для которого isspace()возвращает значение true или grep '[[:space:]]'соответствует ), это стандартное поведение разделения слов , как указано по POSIX :последовательности IFS -пробельные символы (пробельные символы, присутствующие в $IFS), обрабатываются как один разделитель и начальные, конечные единицы или единицы по обе стороны от символы IFS без пробела -игнорируются.

Первоначально в ksh, откуда происходит эта «функция»¹, эта специальная обработка ограничивалась символами TAB, NL и пробелами, но POSIX изменила ее на любой символ, который считается пробелом в локали. Однако на практике большинство оболочек по-прежнему делают это только для TAB/NL/SPC (, которые также имеют значение по умолчанию$IFS). Единственное исключение (единственная оболочка, совместимая с POSIX -в этом отношении ), которую я знаю, это yash.

В bash поведение изменилось в версии 5.0, и теперь bash ведет себя как ksh93 в том, что специальная обработка применяется к пробельным символам, но только к тем, которые закодированы в одном байте.

Сравните обработку символа en space и символа возврата каретки :

$ s=$'\u2000' a="a${s}${s}b" bash -c 'IFS=$s; [[ $s = [[:space:]] ]] && echo yes;  printf "<%s>\n" $a'
yes
<a>
<>
<b>
$ s=$'\r' a="a${s}${s}b" bash -c 'IFS=$s; [[ $s = [[:space:]] ]] && echo yes;  printf "<%s>\n" $a'
yes
<a>
<b>

Таким образом, в bash5.0+, чтобы bashбольше не придавалось особого отношения к TAB, вам необходимо создать локаль, в которой TAB не считается пробелом. Даже в этом случае этого было бы недостаточно, так как даже для символов, отличных от -, пробелов, например ,вместо TAB, a,b,c,разбивается на a, bи cвместо a, b, cи пустая строка .

Здесь лучше использовать оболочку, в которой можно отключить специальную обработку пробельных символов в отношении разделения IFS, или иметь правильный оператор разделения, или, что еще лучше, как говорит @cas, использовать правильный язык программирования, такой как perlвместо оболочки, которая для этого не предназначена.

В ksh93 или zsh специальную обработку можно удалить, удвоив соответствующий символ в $IFS. zshРазбиение слов также не отбрасывает завершающий пустой элемент. Так в zsh,

IFS=$'\t\t' read -rA array

действительно сработает. Там вы также можете сделать:

IFS= read -r line && array=( "${(@ps[\t])line}" )

Чтобы прочитать строку, а затем разделить ее с помощью флага раскрытия параметра s.

В ksh93 вы могли выполнить разбиение с помощью:

set -o noglob
IFS=$'\t\t'
IFS= read -r line && array=( $line'' )

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

ksh93 поддерживает многомерные массивы, которые могут вам помочь:

i=0
IFS=$'\t\t'; set -o noglob
typeset -a array=()
while IFS= read -r line; do
  array[i++]=( $line'' )
done < file.tsv

А затем ${array[2][5]}даст вам, например, 6-е поле 3-й строки.


¹ Разделение IFS -изначально происходит из оболочки Bourne, но в оболочке Bourne все символы получили эту обработку, а не только TAB/NL/SPC

3
28.07.2021, 11:25

Использование Raku (, ранее известного как Perl _6)

Весь приведенный ниже код выполняется в командной строке bash:

raku -e 'my @a = lines>>.split("\t");.raku.put for @a;'  test_tsv.tsv

ВОЗВРАТ:

$(("1", "A", "J", "", "", "", "1").Seq)
$(("2", "B", "K", "N", "", "", "1").Seq)
$(("3", "C", "L", "O", "P", "Q", "1").Seq)

Выше строки разделены на вкладки \t. Метод .rakuиспользуется для визуализации пустых строк. Гипероператор >>— это сокращение от отображения, как в .map(*.split("\t")). Приведенный выше код возвращает объект с 3 .Seqэлементами, однако его легко привести к списку:

raku -e 'my @a = lines>>.split("\t");.list.raku.put for @a;'  test_tsv.tsv
("1", "A", "J", "", "", "", "1")
("2", "B", "K", "N", "", "", "1")
("3", "C", "L", "O", "P", "Q", "1")

Метод elemsиспользуется в Raku для получения количества элементов, либо элементов всего объекта массива, либо элементов каждой «линии»:

raku -e 'my @a = lines>>.split("\t"); @a.elems.put;'  test_tsv.tsv
3
raku -e 'my @a = lines>>.split("\t"); @a>>.elems.put;'  test_tsv.tsv
7 7 7

Как только вы убедитесь, что получили желаемую структуру, можно легко упростить код или добавить/удалить идиомы Perlish/Rakuish, такие как for(, чтобы получить желаемый результат для визуализации или дальнейших манипуляций):

raku -e 'my @a = lines>>.split("\t");.put for @a;'  test_tsv.tsv
1 A J    1
2 B K N   1
3 C L O P Q 1

raku -e 'my @a = lines>>.split("\t"); @a>>.list.raku.put;'  test_tsv.tsv
(("1", "A", "J", "", "", "", "1"), ("2", "B", "K", "N", "", "", "1"), ("3", "C", "L", "O", "P", "Q", "1"))

https://raku.org/
https://docs.raku.org/

3
28.07.2021, 11:25

Теги

Похожие вопросы