如何使用awk对单词的字符进行排序?
How to sort the characters of a word using awk?
我似乎找不到任何方法来根据 awk 中的字符对单词进行排序。
例如,如果单词是“hello”,那么其排序后的等价词是“ehllo”。如何在 awk 中实现这一点?
gawk 会更可行,它包括 asort
函数来对数组进行排序:
awk 'BEGIN{FS=OFS=ORS=""}{split([=10=],a);asort(a);for(i in a)print a[i]}'<<<hello
这输出:
ehllo
您需要编写一个函数来对单词中的字母进行排序(参见:https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html):
function siw(word, result, arr, arrlen, arridx) {
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
return result
}
并定义一个排序子函数来比较两个单词(参见:https://www.gnu.org/software/gawk/manual/html_node/Array-Sorting-Functions.html):
function compare_by_letters(i1, v1, i2, v2, left, right) {
left = siw(v1)
right = siw(v2)
if (left < right)
return -1
else if (left == right)
return 0
else
return 1
}
并将此函数与 awk 排序函数一起使用:
asort(array_test, array_test_result, "compare_by_letters")
那么,示例程序为:
function siw(word, result, arr, arrlen, arridx) {
result = hash_word[word]
if (result != "") {
return result
}
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
hash_word[word] = result
return result
}
function compare_by_letters(i1, v1, i2, v2, left, right) {
left = siw(v1)
right = siw(v2)
if (left < right)
return -1
else if (left == right)
return 0
else
return 1
}
{
array_test[i++] = [=13=]
}
END {
alen = asort(array_test, array_test_result, "compare_by_letters")
for (aind = 1; aind <= alen; aind++) {
print array_test_result[aind]
}
}
这样执行:
echo -e "fail\nhello\nborn" | awk -f sort_letter.awk
输出:
fail
born
hello
当然,如果你有一个大输入,你可以调整 siw
函数来记住最快计算的结果:
function siw(word, result, arr, arrlen, arridx) {
result = hash_word[word]
if (result != "") {
return result
}
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
hash_word[word] = result
return result
}
另一种选择是 装饰-排序-取消装饰 和 sed
。本质上,您使用 sed
将 "hello"
分成每行一个字符(用换行符 '\n'
装饰每个字符)并将结果传递给 sort
。然后使用 sed
进行反向操作(通过删除 '\n'
取消修饰每一行)将这些行重新连接在一起。
printf "hello" | sed 's/\(.\)/\n/g' | sort | sed '{:a N;s/\n//;ta}'
ehllo
您可以使用多种方法,但这种方法 shell 友好,但该行为需要 GNU sed
。
使用 GNU awk for PROCINFO[]
、“sorted_in”(参见 https://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning)并使用空分隔符拆分,生成字符数组:
$ echo 'hello' |
awk '
BEGIN { PROCINFO["sorted_in"]="@val_str_asc" }
{
split(,chars,"")
word = ""
for (i in chars) {
word = word chars[i]
}
print word
}
'
ehllo
$ echo 'hello' | awk -v ordr='@val_str_asc' 'BEGIN{PROCINFO["sorted_in"]=ordr} {split(,chars,""); word=""; for (i in chars) word=word chars[i]; print word}'
ehllo
$ echo 'hello' | awk -v ordr='@val_str_desc' 'BEGIN{PROCINFO["sorted_in"]=ordr} {split(,chars,""); word=""; for (i in chars) word=word chars[i]; print word}'
ollhe
这里有一个非常非正统的方法,如果你真的想将“hello”排序为“ehllo”:
mawk/mawk2/gawk 'BEGIN { FS="^$"
# to make it AaBbCc… etc; chr(65) = ascii "A"
for (x = 65; x < 91; x++) {
ref = sprintf("%s%c%c",ref, x, x+32)
} } /^[[:alpha:]]$/ { print } /[[:alpha:]][[:alpha:]]+/ {
# for gawk/nawk, feel free to change
# that to /[[:alpha:]]{2,}/
# the >= 2+ condition is to prevent wasting time
# sorting single letter words "A" and "I"
s=""; x=1; len=length(inp=[=10=]);
while ( len && (x<53) ) {
if (inp~(ch = substr(ref,x++,1))) {
while ( sub(ch,"",inp) ) {
s = s ch;
len -= 1 ;
} } }
print s }'
我知道这是进行选择排序的一种极其低效的方法。潜在的节省时间源于在所有字母完成时立即结束循环,而不是每次都迭代所有 52 个字母。缺点是它不会预先分析输入
(例如,如果您检测到该行只是小写字母,那么您可以使用仅小写字母的循环来加速它)
好处是它消除了对自定义函数的需要,消除了任何 gawk 依赖性,也消除了将每一行拆分为数组(或每个字符到其自己的字段)的需要
我的意思是,从技术上讲,可以将 FS 设置为空字符串,从而自动将 NF 作为字符串长度。但有时如果输入有点大,它可能会很慢。如果您需要 unicode 支持,那么基于 match()
的方法更可取。
- 添加 (x<53) 条件以防止 运行-away 无限循环,以防输入不是纯 ASCII 字母
我似乎找不到任何方法来根据 awk 中的字符对单词进行排序。 例如,如果单词是“hello”,那么其排序后的等价词是“ehllo”。如何在 awk 中实现这一点?
gawk 会更可行,它包括 asort
函数来对数组进行排序:
awk 'BEGIN{FS=OFS=ORS=""}{split([=10=],a);asort(a);for(i in a)print a[i]}'<<<hello
这输出:
ehllo
您需要编写一个函数来对单词中的字母进行排序(参见:https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html):
function siw(word, result, arr, arrlen, arridx) {
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
return result
}
并定义一个排序子函数来比较两个单词(参见:https://www.gnu.org/software/gawk/manual/html_node/Array-Sorting-Functions.html):
function compare_by_letters(i1, v1, i2, v2, left, right) {
left = siw(v1)
right = siw(v2)
if (left < right)
return -1
else if (left == right)
return 0
else
return 1
}
并将此函数与 awk 排序函数一起使用:
asort(array_test, array_test_result, "compare_by_letters")
那么,示例程序为:
function siw(word, result, arr, arrlen, arridx) {
result = hash_word[word]
if (result != "") {
return result
}
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
hash_word[word] = result
return result
}
function compare_by_letters(i1, v1, i2, v2, left, right) {
left = siw(v1)
right = siw(v2)
if (left < right)
return -1
else if (left == right)
return 0
else
return 1
}
{
array_test[i++] = [=13=]
}
END {
alen = asort(array_test, array_test_result, "compare_by_letters")
for (aind = 1; aind <= alen; aind++) {
print array_test_result[aind]
}
}
这样执行:
echo -e "fail\nhello\nborn" | awk -f sort_letter.awk
输出:
fail
born
hello
当然,如果你有一个大输入,你可以调整 siw
函数来记住最快计算的结果:
function siw(word, result, arr, arrlen, arridx) {
result = hash_word[word]
if (result != "") {
return result
}
split(word, arr, "")
arrlen = asort(arr)
for (arridx = 1; arridx <= arrlen; arridx++) {
result = result arr[arridx]
}
hash_word[word] = result
return result
}
另一种选择是 装饰-排序-取消装饰 和 sed
。本质上,您使用 sed
将 "hello"
分成每行一个字符(用换行符 '\n'
装饰每个字符)并将结果传递给 sort
。然后使用 sed
进行反向操作(通过删除 '\n'
取消修饰每一行)将这些行重新连接在一起。
printf "hello" | sed 's/\(.\)/\n/g' | sort | sed '{:a N;s/\n//;ta}'
ehllo
您可以使用多种方法,但这种方法 shell 友好,但该行为需要 GNU sed
。
使用 GNU awk for PROCINFO[]
、“sorted_in”(参见 https://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Scanning)并使用空分隔符拆分,生成字符数组:
$ echo 'hello' |
awk '
BEGIN { PROCINFO["sorted_in"]="@val_str_asc" }
{
split(,chars,"")
word = ""
for (i in chars) {
word = word chars[i]
}
print word
}
'
ehllo
$ echo 'hello' | awk -v ordr='@val_str_asc' 'BEGIN{PROCINFO["sorted_in"]=ordr} {split(,chars,""); word=""; for (i in chars) word=word chars[i]; print word}'
ehllo
$ echo 'hello' | awk -v ordr='@val_str_desc' 'BEGIN{PROCINFO["sorted_in"]=ordr} {split(,chars,""); word=""; for (i in chars) word=word chars[i]; print word}'
ollhe
这里有一个非常非正统的方法,如果你真的想将“hello”排序为“ehllo”:
mawk/mawk2/gawk 'BEGIN { FS="^$"
# to make it AaBbCc… etc; chr(65) = ascii "A"
for (x = 65; x < 91; x++) {
ref = sprintf("%s%c%c",ref, x, x+32)
} } /^[[:alpha:]]$/ { print } /[[:alpha:]][[:alpha:]]+/ {
# for gawk/nawk, feel free to change
# that to /[[:alpha:]]{2,}/
# the >= 2+ condition is to prevent wasting time
# sorting single letter words "A" and "I"
s=""; x=1; len=length(inp=[=10=]);
while ( len && (x<53) ) {
if (inp~(ch = substr(ref,x++,1))) {
while ( sub(ch,"",inp) ) {
s = s ch;
len -= 1 ;
} } }
print s }'
我知道这是进行选择排序的一种极其低效的方法。潜在的节省时间源于在所有字母完成时立即结束循环,而不是每次都迭代所有 52 个字母。缺点是它不会预先分析输入
(例如,如果您检测到该行只是小写字母,那么您可以使用仅小写字母的循环来加速它)
好处是它消除了对自定义函数的需要,消除了任何 gawk 依赖性,也消除了将每一行拆分为数组(或每个字符到其自己的字段)的需要
我的意思是,从技术上讲,可以将 FS 设置为空字符串,从而自动将 NF 作为字符串长度。但有时如果输入有点大,它可能会很慢。如果您需要 unicode 支持,那么基于 match()
的方法更可取。
- 添加 (x<53) 条件以防止 运行-away 无限循环,以防输入不是纯 ASCII 字母